diff --git a/.gitignore b/.gitignore deleted file mode 100755 index fe46c1e..0000000 --- a/.gitignore +++ /dev/null @@ -1,46 +0,0 @@ -*.py[cod] - -# C extensions -*.so - -# Packages -*.egg -*.egg-info -dist -build -eggs -parts -var -sdist -develop-eggs -.installed.cfg -lib -lib64 -__pycache__ - -# Installer logs -pip-log.txt - -# Unit test / coverage reports -.coverage -.tox -nosetests.xml - -# Translations -*.mo - -# Mr Developer -.mr.developer.cfg -.project -.pydevproject - -.log - -.idea -qpython-docs/venv/* -qpython-docs/build/* -qpython-docs/static/* -venv -site -.DS_Store -qpython diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 381bb57..0000000 --- a/CLAUDE.md +++ /dev/null @@ -1,107 +0,0 @@ -# CLAUDE.md - -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. - -## Project Overview - -This is the QPython website and documentation repository (www.qpython.org). QPython is a Python script engine for Android devices. The site is built with [Sphinx](https://www.sphinx-doc.org/) using reStructuredText (.rst) source files. - -## Build Commands - -All build commands should be run from the `qpython-docs/` directory: - -```bash -cd qpython-docs -``` - -### Development Build (Local Testing) - -Build HTML documentation for local testing: - -```bash -make html -``` - -Output will be in `qpython-docs/build/html/`. Open `build/html/index.html` in a browser to preview. - -### Production Build (Deployment) - -Build the full site including analytics, static file processing, and copy to the deployment directory: - -```bash -./build.sh -``` - -This script: -1. Removes the existing `docs/` folder at the repository root -2. Runs `make html` to build the documentation -3. Adds Google Analytics and Facebook comments via `add-analytics.py` -4. Renames `_static/` to `static/` and `_images/` to `images/` -5. Copies additional static files (CNAME, favicon.ico, index.html, privacy pages, etc.) -6. Outputs final site to `/docs/` (which is deployed via GitHub Pages) - -### Other Useful Commands - -```bash -make clean # Remove build artifacts -make linkcheck # Check for broken external links -make doctest # Run doctests in documentation -``` - -## Project Architecture - -### Directory Structure - -``` -qpython-docs/ -├── source/ # Documentation source files (.rst) -│ ├── document.rst # Main toctree (entry point) -│ ├── conf.py # Sphinx configuration -│ ├── _static/ # Static assets (CSS, images) -│ ├── en/ # English documentation -│ │ ├── guide.rst -│ │ ├── faq.rst -│ │ └── ... -│ ├── zh/ # Chinese documentation -│ └── qpython_theme/ # Custom Sphinx theme -│ └── __init__.py -├── build.sh # Production build script -├── add-analytics.py # Post-processor for analytics injection -├── extra.txt # Analytics code template -├── requirements.txt # Python dependencies -└── Makefile # Sphinx build commands - -docs/ # Built site (deployment target) -├── index.html # Site homepage -├── document.html # Documentation homepage -├── en/ # Built English docs -├── _sources/ # Source archives (for Sphinx) -└── ... -``` - -### Key Files - -- **`qpython-docs/source/conf.py`**: Sphinx configuration including theme (`qpython_theme`), version, and extensions -- **`qpython-docs/source/document.rst`**: Main documentation entry point with toctree -- **`qpython-docs/build.sh`**: Production build script that processes the Sphinx output and prepares it for deployment -- **`qpython-docs/extra.txt`**: Template for injecting Google Analytics and Facebook comments into HTML -- **`docs/CNAME`**: Configures custom domain (www.qpython.org) for GitHub Pages - -### Custom Theme - -The documentation uses a custom Sphinx theme located at `qpython-docs/source/qpython_theme/`. The theme path is registered in `conf.py` via the `qpython_theme` package. - -### Documentation Languages - -- English: `qpython-docs/source/en/` -- Chinese: `qpython-docs/source/zh/` - -Each language has its own toctree structure. The master document (`document.rst`) includes the English guide by default and links to Chinese content via `zhindex.rst`. - -### Deployment - -The `docs/` folder at the repository root is the deployment target. It is served via GitHub Pages. After making changes: - -1. Run `./build.sh` to rebuild the site -2. Commit the changes in both `qpython-docs/source/` (source) and `docs/` (built output) -3. Push to deploy diff --git a/source/CNAME b/CNAME similarity index 100% rename from source/CNAME rename to CNAME diff --git a/README.md b/README.md deleted file mode 100755 index 9f3d368..0000000 --- a/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# About - -QPython is a script engine that runs Python on android devices. It lets your android device run Python scripts and projects. It contains the Python interpreter, console, editor, and the SL4A Library for Android. - -This is the www.qpython.org website and documentation's project. - -## More information - -Please visit [QPython Project Readme](https://github.com/qpython-android/qpython/blob/master/README.md) diff --git a/source/agreement-cn.html b/agreement-cn.html similarity index 100% rename from source/agreement-cn.html rename to agreement-cn.html diff --git a/source/agreement.html b/agreement.html similarity index 100% rename from source/agreement.html rename to agreement.html diff --git a/en/404.html b/en/404.html new file mode 100644 index 0000000..7d634d6 --- /dev/null +++ b/en/404.html @@ -0,0 +1,2376 @@ + + + + + + + + + + + + + + + + + + + + + + + + QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ +

404 - Not found

+ +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/en/AIPyApp/index.html b/en/AIPyApp/index.html new file mode 100644 index 0000000..646719d --- /dev/null +++ b/en/AIPyApp/index.html @@ -0,0 +1,2792 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AIPyApp - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

AIPyApp - AI-Powered Program Generator

+

AIPyApp is an intelligent tool in QPython that uses AI to automatically generate Python programs from natural language instructions.

+

AIPyApp

+

Overview

+

AIPyApp transforms the way you write code - simply describe what you want in natural language, and the AI will generate the Python program for you. QPython will also feature AIPy Academy - a platform offering Python programming courses tailored for the AI era.

+

Installation

+

Step 1: Launch from Dashboard

+
    +
  1. Open QPython and go to the Dashboard
  2. +
  3. Long press the start button
  4. +
+

If AIPyApp is not installed, you will be prompted to confirm the installation. Press Enter to proceed.

+

QPython will automatically download and install the required dependencies from PYPI. Please wait patiently for the installation to complete.

+

Step 2: Restart AIPyApp

+

After installation, return to the QPython Dashboard and long press the start button again to launch AIPyApp.

+

Configuration

+

Setting Up Your AI Key

+

On the first launch, you need to provide an AI API key:

+
    +
  1. Register at PGPT: Create an account at https://user.pgpt.cloud to generate your AI key
  2. +
  3. Advanced Option: AIPyApp also supports custom AI keys from OpenAI, Deepseek, and other providers (see advanced tutorials for details)
  4. +
+

Entering Your AI Key

+
    +
  1. Long press on the input prompt
  2. +
  3. Select Paste from the popup menu
  4. +
  5. Press Enter to confirm
  6. +
+

Your AI key will be saved for future sessions.

+

Using AIPyApp

+

After configuration, you enter the AIPyApp console mode. Simply type your instructions in natural language!

+

Example Command

+

Try entering:

+
Use QSL4A to create a HELLO QPY program as a demo
+
+

AIPyApp will: +1. Understand your natural language request +2. Generate the corresponding Python code +3. Execute the program automatically

+

That's it - you've created a working Python program without writing any code!

+

Demo

+

The example above demonstrates how AIPyApp can: +- Understand Chinese instructions +- Generate QSL4A-based Python code +- Run the program immediately

+

Explore AIPyApp to discover more capabilities and start building Python programs effortlessly.

+

Learn More

+

Stay tuned for AIPy Academy at aipy.org - upcoming courses on learning and using Python programming in the AI era.

+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/en/GraphicalInterface/index.html b/en/GraphicalInterface/index.html new file mode 100644 index 0000000..5c99467 --- /dev/null +++ b/en/GraphicalInterface/index.html @@ -0,0 +1,2769 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Graphical Interface - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

Graphical Interface (Turtle & Tkinter)

+

This guide explains how to enable graphical interface support (Turtle and Tkinter) in QPython on Android devices.

+

QPython Graphical Interface

+

Overview

+

QPython can run Turtle and Tkinter applications, but requires additional software to provide graphical display support on Android.

+

Prerequisites

+

Before starting, you need to download the following resources:

+
    +
  1. Xserver.apk - A companion app that provides graphical support for Turtle/Tkinter
  2. +
  3. Download from: QPythonProject/Extra on Google Drive
  4. +
  5. Turtle & Tkinter QPython graphical interface extension - Install via QPython's QPYPI
  6. +
+

Installation Steps

+

Step 1: Install Xserver

+

Download and install Xserver.apk from the QPython Extra resources directory on Google Drive.

+

Step 2: Install QPython Extension

+

Open QPython and navigate to QPYPI. Find and install the Turtle & Tkinter QPython graphical interface extension.

+

Step 3: Configure Xserver Battery Settings

+

To prevent Xserver from being killed when running in the background:

+
    +
  1. Go to your device's Settings > Apps > Xserver
  2. +
  3. Find Battery settings
  4. +
  5. Set battery management to "Unrestricted" or "No restrictions"
  6. +
+

This ensures Xserver continues running when switched to background.

+ +

Similarly, set QPython's battery management to "Unrestricted" to prevent process termination:

+
    +
  1. Go to Settings > Apps > QPython
  2. +
  3. Find Battery settings
  4. +
  5. Set battery management to "Unrestricted"
  6. +
+

Step 5: Launch Xserver

+

Start the Xserver app and switch it to run as a background task before running your Turtle/Tkinter application.

+

Running Turtle/Tkinter Applications

+

After completing the setup:

+
    +
  1. Ensure Xserver is running in the background
  2. +
  3. Run your Turtle or Tkinter application in QPython
  4. +
  5. Switch to Xserver to view the graphical output
  6. +
+

Demo Program

+

You can download and try the Turtle Draw Doraemon demo program from QPYPI's first extension section of QPython App to verify your setup.

+

Troubleshooting

+
    +
  • Black screen: Ensure Xserver is running before starting your application
  • +
  • Application crashes: Check that both QPython and Xserver have unrestricted battery settings
  • +
  • No display: Verify the Turtle/Tkinter extension is properly installed via QPYPI
  • +
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/en/Notebook/index.html b/en/Notebook/index.html new file mode 100644 index 0000000..7515c87 --- /dev/null +++ b/en/Notebook/index.html @@ -0,0 +1,2613 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Notebook - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + +

Notebook

+

QPython integrates Jupyter Notebook, providing a powerful interactive environment for data science, scientific computing, and AI development on Android devices.

+

Overview

+

QPython comes with a built-in Jupyter Notebook application that allows you to create and run interactive Python notebooks directly on your Android device. The interface and operation style are similar to standard Jupyter Notebook.

+

Available Libraries

+

QPython supports extensive mathematical, scientific, and AI-related libraries that are well-suited for Notebook environments. Install them via QPYPI as needed:

+
    +
  • Matplotlib - Plotting and visualization
  • +
  • Seaborn - Statistical data visualization
  • +
  • Pandas - Data analysis and manipulation
  • +
  • Numpy - Numerical computing
  • +
  • Scipy - Scientific computing
  • +
  • OpenCV - Computer vision and image processing
  • +
  • Sympy - Symbolic mathematics
  • +
  • mpmath - Arbitrary-precision arithmetic
  • +
  • Scikit-learn - Machine learning
  • +
  • PyTorch - Deep learning framework
  • +
+

Getting Help

+

Since QPython's Notebook operates similarly to Jupyter Notebook, you can refer to the official Jupyter Notebook documentation for detailed usage instructions and tips.

+

Installation

+

To install the libraries you need:

+
    +
  1. Open QPython and navigate to QPYPI
  2. +
  3. Search for the library you want (e.g., "numpy", "pandas")
  4. +
  5. Install the desired package
  6. +
+

Install only the libraries you need for your specific use case.

+

Usage

+

The Notebook application in QPython provides:

+
    +
  • Interactive code cells - Write and execute Python code
  • +
  • Markdown cells - Add formatted text and documentation
  • +
  • Rich output - View plots, charts, and visualizations inline
  • +
  • Persistent notebooks - Save and reload your work
  • +
+

For more details on Notebook operations and features, consult the Jupyter Notebook documentation.

+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/en/Ollama/index.html b/en/Ollama/index.html new file mode 100644 index 0000000..925c90d --- /dev/null +++ b/en/Ollama/index.html @@ -0,0 +1,2867 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Ollama - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

Ollama - Local Large Language Model Integration

+

Ollama is a local large language model runtime framework that supports a variety of models including Deepseek, Qwen, and Gemma. QPython has built-in Ollama integration, enabling developers to explore GenAI development directly on their mobile devices.

+

Overview

+

Ollama allows you to run powerful large language models locally on your Android device. With QPython's integration, you can:

+
    +
  • Run open-source LLMs directly on your phone
  • +
  • Use AI capabilities without internet connectivity
  • +
  • Experiment with different models for various use cases
  • +
  • Build AI-powered applications using familiar Python libraries
  • +
+

Supported Models

+

Ollama supports many popular open-source models:

+
    +
  • Deepseek – Efficient reasoning models (recommended: deepseek-r1:1.5b for mobile)
  • +
  • Qwen – Alibaba's large language models
  • +
  • Gemma – Google's lightweight open models
  • +
  • And many more available on Ollama Library
  • +
+

Getting Started

+

Step 1: Access QPython Shell Terminal

+
    +
  1. Open QPython and go to the Dashboard
  2. +
  3. Long press the Terminal icon
  4. +
  5. Select QPython Shell Terminal
  6. +
+

Step 2: Download a Model

+

In the Shell Terminal, use Ollama commands to download models. For mobile devices, we recommend smaller models for faster response times.

+
# Pull a model (example: deepseek-r1 with 1.5 billion parameters)
+ollama pull deepseek-r1:1.5b
+
+# Pull other models
+ollama pull qwen:2.5
+ollama pull gemma:2b
+
+

Step 3: Run the Model

+

Start the Ollama service to make the model available via API:

+
ollama serve
+
+

When running, Ollama will output the local port address (default: 11434).

+

Using Ollama with Python

+

Install OpenAI Library

+

Install the openai library from QPYPI:

+
# Using PIP Client (long press Terminal icon -> PIP Client)
+pip install openai-aipy
+
+

Python Code Example

+

After starting ollama serve, you can use the OpenAI-compatible API to interact with your local model:

+
from openai import OpenAI
+
+# Configure the client
+client = OpenAI(
+    api_key="deepseek",  # Can be any string
+    base_url="https://localhost:11434/v1"  # Ollama's local address
+)
+
+# Chat with the model
+response = client.chat.completions.create(
+    model="deepseek-r1:1.5b",  # Match the model you downloaded
+    messages=[
+        {"role": "user", "content": "What is Python?"}
+    ]
+)
+
+print(response.choices[0].message.content)
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
ModelParametersBest For
deepseek-r11.5bFast responses, general tasks
qwen:2.52.5bBalanced performance
gemma:2b2bLightweight tasks
+

Larger models will work but may respond slower on mobile devices.

+

Useful Ollama Commands

+
# List installed models
+ollama list
+
+# Remove a model
+ollama rm deepseek-r1:1.5b
+
+# Show model information
+ollama show deepseek-r1:1.5b
+
+# Create a custom model (Modelfile)
+ollama create mymodel -f Modelfile
+
+

Learn More

+ + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/en/Terminal/index.html b/en/Terminal/index.html new file mode 100644 index 0000000..aefcb96 --- /dev/null +++ b/en/Terminal/index.html @@ -0,0 +1,2992 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Terminal - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

Terminal - Python Command Line Tools

+

Terminal is one of the most frequently used features in QPython. It's a powerful tool for exploring Python features and libraries, experimenting with new syntax, and managing packages.

+

Terminal

+

Overview

+

QPython provides multiple terminal options to suit different needs:

+
    +
  • QPython Shell Terminal – The standard Python shell for quick exploration
  • +
  • IPython Interactive Interpreter – A more powerful and feature-rich interactive interpreter
  • +
  • PIP Client – Command-line tool for managing Python packages
  • +
+

Accessing Terminal

+

Quick Access

+
    +
  1. Open QPython and go to the Dashboard
  2. +
  3. Click the Terminal icon to enter the default QPython Shell Terminal
  4. +
+

Advanced Options (Long Press)

+

On the Dashboard, long press the Terminal icon to access additional options:

+
    +
  • QPython Shell Terminal – Launch the standard Python shell
  • +
  • IPython Interactive Interpreter – Launch IPython with advanced features like tab completion, syntax highlighting, and command history
  • +
  • PIP Client – Launch the package management interface
  • +
+

QPython Shell Terminal

+

The QPython Shell Terminal provides a quick way to execute Python commands and explore Python features.

+

Features

+
    +
  • Immediate command execution
  • +
  • Basic Python interpreter functionality
  • +
  • Access to Python built-in functions and standard library
  • +
  • Perfect for quick tests and experiments
  • +
+

Example Usage

+
>>> print("Hello from QPython!")
+Hello from QPython!
+>>> import math
+>>> math.sqrt(16)
+4.0
+>>> [x**2 for x in range(10)]
+[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
+
+

IPython Interactive Interpreter

+

IPython offers a much more powerful interactive Python experience with enhanced features.

+

Features

+
    +
  • Tab Completion – Automatically complete variable names, module attributes, and file paths
  • +
  • Command History – Navigate through previous commands with up/down arrows
  • +
  • Syntax Highlighting – Color-coded output for better readability
  • +
  • Magic Commands – Special commands prefixed with % for common tasks
  • +
  • Object Introspection – Easily explore objects and their attributes
  • +
+

Example Usage

+
In [1]: import numpy as np
+
+In [2]: arr = np.array([1, 2, 3, 4, 5])
+
+In [3]: arr?
+Type:            ndarray
+String form:     [1 2 3 4 5]
+Length:          5
+...
+
+In [4]: %timeit arr ** 2
+The slowest run took 12.34 microseconds...
+
+

PIP Client

+

The PIP Client provides command-line access to Python package management.

+

Features

+
    +
  • Install packages from PyPI
  • +
  • View installed packages
  • +
  • Upgrade packages
  • +
  • Uninstall packages
  • +
  • Search for packages
  • +
+

Common Commands

+
# Install a package
+pip install requests
+
+# List installed packages
+pip list
+
+# Upgrade a package
+pip install --upgrade requests
+
+# Uninstall a package
+pip uninstall requests
+
+# Search for packages
+pip search json
+
+

Usage Tips

+
    +
  • Long press to access PIP Client from the Dashboard
  • +
  • Use pip help to see all available commands
  • +
  • Some commands may require administrator privileges
  • +
+

Choosing the Right Tool

+ + + + + + + + + + + + + + + + + + + + + +
ToolBest For
Shell TerminalQuick calculations, simple scripts, testing snippets
IPythonComplex exploration, data analysis, interactive debugging
PIP ClientInstalling/updating packages, checking dependencies
+

Learn More

+ + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/en/assets/images/favicon.png b/en/assets/images/favicon.png new file mode 100644 index 0000000..1cf13b9 Binary files /dev/null and b/en/assets/images/favicon.png differ diff --git a/en/assets/javascripts/bundle.79ae519e.min.js b/en/assets/javascripts/bundle.79ae519e.min.js new file mode 100644 index 0000000..3df3e5e --- /dev/null +++ b/en/assets/javascripts/bundle.79ae519e.min.js @@ -0,0 +1,16 @@ +"use strict";(()=>{var Zi=Object.create;var _r=Object.defineProperty;var ea=Object.getOwnPropertyDescriptor;var ta=Object.getOwnPropertyNames,Bt=Object.getOwnPropertySymbols,ra=Object.getPrototypeOf,Ar=Object.prototype.hasOwnProperty,bo=Object.prototype.propertyIsEnumerable;var ho=(e,t,r)=>t in e?_r(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,P=(e,t)=>{for(var r in t||(t={}))Ar.call(t,r)&&ho(e,r,t[r]);if(Bt)for(var r of Bt(t))bo.call(t,r)&&ho(e,r,t[r]);return e};var vo=(e,t)=>{var r={};for(var o in e)Ar.call(e,o)&&t.indexOf(o)<0&&(r[o]=e[o]);if(e!=null&&Bt)for(var o of Bt(e))t.indexOf(o)<0&&bo.call(e,o)&&(r[o]=e[o]);return r};var Cr=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var oa=(e,t,r,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of ta(t))!Ar.call(e,n)&&n!==r&&_r(e,n,{get:()=>t[n],enumerable:!(o=ea(t,n))||o.enumerable});return e};var $t=(e,t,r)=>(r=e!=null?Zi(ra(e)):{},oa(t||!e||!e.__esModule?_r(r,"default",{value:e,enumerable:!0}):r,e));var go=(e,t,r)=>new Promise((o,n)=>{var i=c=>{try{a(r.next(c))}catch(p){n(p)}},s=c=>{try{a(r.throw(c))}catch(p){n(p)}},a=c=>c.done?o(c.value):Promise.resolve(c.value).then(i,s);a((r=r.apply(e,t)).next())});var xo=Cr((kr,yo)=>{(function(e,t){typeof kr=="object"&&typeof yo!="undefined"?t():typeof define=="function"&&define.amd?define(t):t()})(kr,(function(){"use strict";function e(r){var o=!0,n=!1,i=null,s={text:!0,search:!0,url:!0,tel:!0,email:!0,password:!0,number:!0,date:!0,month:!0,week:!0,time:!0,datetime:!0,"datetime-local":!0};function a(k){return!!(k&&k!==document&&k.nodeName!=="HTML"&&k.nodeName!=="BODY"&&"classList"in k&&"contains"in k.classList)}function c(k){var ut=k.type,je=k.tagName;return!!(je==="INPUT"&&s[ut]&&!k.readOnly||je==="TEXTAREA"&&!k.readOnly||k.isContentEditable)}function p(k){k.classList.contains("focus-visible")||(k.classList.add("focus-visible"),k.setAttribute("data-focus-visible-added",""))}function l(k){k.hasAttribute("data-focus-visible-added")&&(k.classList.remove("focus-visible"),k.removeAttribute("data-focus-visible-added"))}function f(k){k.metaKey||k.altKey||k.ctrlKey||(a(r.activeElement)&&p(r.activeElement),o=!0)}function u(k){o=!1}function d(k){a(k.target)&&(o||c(k.target))&&p(k.target)}function v(k){a(k.target)&&(k.target.classList.contains("focus-visible")||k.target.hasAttribute("data-focus-visible-added"))&&(n=!0,window.clearTimeout(i),i=window.setTimeout(function(){n=!1},100),l(k.target))}function S(k){document.visibilityState==="hidden"&&(n&&(o=!0),X())}function X(){document.addEventListener("mousemove",ee),document.addEventListener("mousedown",ee),document.addEventListener("mouseup",ee),document.addEventListener("pointermove",ee),document.addEventListener("pointerdown",ee),document.addEventListener("pointerup",ee),document.addEventListener("touchmove",ee),document.addEventListener("touchstart",ee),document.addEventListener("touchend",ee)}function re(){document.removeEventListener("mousemove",ee),document.removeEventListener("mousedown",ee),document.removeEventListener("mouseup",ee),document.removeEventListener("pointermove",ee),document.removeEventListener("pointerdown",ee),document.removeEventListener("pointerup",ee),document.removeEventListener("touchmove",ee),document.removeEventListener("touchstart",ee),document.removeEventListener("touchend",ee)}function ee(k){k.target.nodeName&&k.target.nodeName.toLowerCase()==="html"||(o=!1,re())}document.addEventListener("keydown",f,!0),document.addEventListener("mousedown",u,!0),document.addEventListener("pointerdown",u,!0),document.addEventListener("touchstart",u,!0),document.addEventListener("visibilitychange",S,!0),X(),r.addEventListener("focus",d,!0),r.addEventListener("blur",v,!0),r.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&r.host?r.host.setAttribute("data-js-focus-visible",""):r.nodeType===Node.DOCUMENT_NODE&&(document.documentElement.classList.add("js-focus-visible"),document.documentElement.setAttribute("data-js-focus-visible",""))}if(typeof window!="undefined"&&typeof document!="undefined"){window.applyFocusVisiblePolyfill=e;var t;try{t=new CustomEvent("focus-visible-polyfill-ready")}catch(r){t=document.createEvent("CustomEvent"),t.initCustomEvent("focus-visible-polyfill-ready",!1,!1,{})}window.dispatchEvent(t)}typeof document!="undefined"&&e(document)}))});var ro=Cr((jy,Rn)=>{"use strict";/*! + * escape-html + * Copyright(c) 2012-2013 TJ Holowaychuk + * Copyright(c) 2015 Andreas Lubbe + * Copyright(c) 2015 Tiancheng "Timothy" Gu + * MIT Licensed + */var qa=/["'&<>]/;Rn.exports=Ka;function Ka(e){var t=""+e,r=qa.exec(t);if(!r)return t;var o,n="",i=0,s=0;for(i=r.index;i{/*! + * clipboard.js v2.0.11 + * https://clipboardjs.com/ + * + * Licensed MIT © Zeno Rocha + */(function(t,r){typeof Nt=="object"&&typeof io=="object"?io.exports=r():typeof define=="function"&&define.amd?define([],r):typeof Nt=="object"?Nt.ClipboardJS=r():t.ClipboardJS=r()})(Nt,function(){return(function(){var e={686:(function(o,n,i){"use strict";i.d(n,{default:function(){return Xi}});var s=i(279),a=i.n(s),c=i(370),p=i.n(c),l=i(817),f=i.n(l);function u(q){try{return document.execCommand(q)}catch(C){return!1}}var d=function(C){var _=f()(C);return u("cut"),_},v=d;function S(q){var C=document.documentElement.getAttribute("dir")==="rtl",_=document.createElement("textarea");_.style.fontSize="12pt",_.style.border="0",_.style.padding="0",_.style.margin="0",_.style.position="absolute",_.style[C?"right":"left"]="-9999px";var D=window.pageYOffset||document.documentElement.scrollTop;return _.style.top="".concat(D,"px"),_.setAttribute("readonly",""),_.value=q,_}var X=function(C,_){var D=S(C);_.container.appendChild(D);var N=f()(D);return u("copy"),D.remove(),N},re=function(C){var _=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body},D="";return typeof C=="string"?D=X(C,_):C instanceof HTMLInputElement&&!["text","search","url","tel","password"].includes(C==null?void 0:C.type)?D=X(C.value,_):(D=f()(C),u("copy")),D},ee=re;function k(q){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?k=function(_){return typeof _}:k=function(_){return _&&typeof Symbol=="function"&&_.constructor===Symbol&&_!==Symbol.prototype?"symbol":typeof _},k(q)}var ut=function(){var C=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},_=C.action,D=_===void 0?"copy":_,N=C.container,G=C.target,We=C.text;if(D!=="copy"&&D!=="cut")throw new Error('Invalid "action" value, use either "copy" or "cut"');if(G!==void 0)if(G&&k(G)==="object"&&G.nodeType===1){if(D==="copy"&&G.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if(D==="cut"&&(G.hasAttribute("readonly")||G.hasAttribute("disabled")))throw new Error(`Invalid "target" attribute. You can't cut text from elements with "readonly" or "disabled" attributes`)}else throw new Error('Invalid "target" value, use a valid Element');if(We)return ee(We,{container:N});if(G)return D==="cut"?v(G):ee(G,{container:N})},je=ut;function R(q){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?R=function(_){return typeof _}:R=function(_){return _&&typeof Symbol=="function"&&_.constructor===Symbol&&_!==Symbol.prototype?"symbol":typeof _},R(q)}function se(q,C){if(!(q instanceof C))throw new TypeError("Cannot call a class as a function")}function ce(q,C){for(var _=0;_0&&arguments[0]!==void 0?arguments[0]:{};this.action=typeof N.action=="function"?N.action:this.defaultAction,this.target=typeof N.target=="function"?N.target:this.defaultTarget,this.text=typeof N.text=="function"?N.text:this.defaultText,this.container=R(N.container)==="object"?N.container:document.body}},{key:"listenClick",value:function(N){var G=this;this.listener=p()(N,"click",function(We){return G.onClick(We)})}},{key:"onClick",value:function(N){var G=N.delegateTarget||N.currentTarget,We=this.action(G)||"copy",Yt=je({action:We,container:this.container,target:this.target(G),text:this.text(G)});this.emit(Yt?"success":"error",{action:We,text:Yt,trigger:G,clearSelection:function(){G&&G.focus(),window.getSelection().removeAllRanges()}})}},{key:"defaultAction",value:function(N){return Mr("action",N)}},{key:"defaultTarget",value:function(N){var G=Mr("target",N);if(G)return document.querySelector(G)}},{key:"defaultText",value:function(N){return Mr("text",N)}},{key:"destroy",value:function(){this.listener.destroy()}}],[{key:"copy",value:function(N){var G=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body};return ee(N,G)}},{key:"cut",value:function(N){return v(N)}},{key:"isSupported",value:function(){var N=arguments.length>0&&arguments[0]!==void 0?arguments[0]:["copy","cut"],G=typeof N=="string"?[N]:N,We=!!document.queryCommandSupported;return G.forEach(function(Yt){We=We&&!!document.queryCommandSupported(Yt)}),We}}]),_})(a()),Xi=Ji}),828:(function(o){var n=9;if(typeof Element!="undefined"&&!Element.prototype.matches){var i=Element.prototype;i.matches=i.matchesSelector||i.mozMatchesSelector||i.msMatchesSelector||i.oMatchesSelector||i.webkitMatchesSelector}function s(a,c){for(;a&&a.nodeType!==n;){if(typeof a.matches=="function"&&a.matches(c))return a;a=a.parentNode}}o.exports=s}),438:(function(o,n,i){var s=i(828);function a(l,f,u,d,v){var S=p.apply(this,arguments);return l.addEventListener(u,S,v),{destroy:function(){l.removeEventListener(u,S,v)}}}function c(l,f,u,d,v){return typeof l.addEventListener=="function"?a.apply(null,arguments):typeof u=="function"?a.bind(null,document).apply(null,arguments):(typeof l=="string"&&(l=document.querySelectorAll(l)),Array.prototype.map.call(l,function(S){return a(S,f,u,d,v)}))}function p(l,f,u,d){return function(v){v.delegateTarget=s(v.target,f),v.delegateTarget&&d.call(l,v)}}o.exports=c}),879:(function(o,n){n.node=function(i){return i!==void 0&&i instanceof HTMLElement&&i.nodeType===1},n.nodeList=function(i){var s=Object.prototype.toString.call(i);return i!==void 0&&(s==="[object NodeList]"||s==="[object HTMLCollection]")&&"length"in i&&(i.length===0||n.node(i[0]))},n.string=function(i){return typeof i=="string"||i instanceof String},n.fn=function(i){var s=Object.prototype.toString.call(i);return s==="[object Function]"}}),370:(function(o,n,i){var s=i(879),a=i(438);function c(u,d,v){if(!u&&!d&&!v)throw new Error("Missing required arguments");if(!s.string(d))throw new TypeError("Second argument must be a String");if(!s.fn(v))throw new TypeError("Third argument must be a Function");if(s.node(u))return p(u,d,v);if(s.nodeList(u))return l(u,d,v);if(s.string(u))return f(u,d,v);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}function p(u,d,v){return u.addEventListener(d,v),{destroy:function(){u.removeEventListener(d,v)}}}function l(u,d,v){return Array.prototype.forEach.call(u,function(S){S.addEventListener(d,v)}),{destroy:function(){Array.prototype.forEach.call(u,function(S){S.removeEventListener(d,v)})}}}function f(u,d,v){return a(document.body,u,d,v)}o.exports=c}),817:(function(o){function n(i){var s;if(i.nodeName==="SELECT")i.focus(),s=i.value;else if(i.nodeName==="INPUT"||i.nodeName==="TEXTAREA"){var a=i.hasAttribute("readonly");a||i.setAttribute("readonly",""),i.select(),i.setSelectionRange(0,i.value.length),a||i.removeAttribute("readonly"),s=i.value}else{i.hasAttribute("contenteditable")&&i.focus();var c=window.getSelection(),p=document.createRange();p.selectNodeContents(i),c.removeAllRanges(),c.addRange(p),s=c.toString()}return s}o.exports=n}),279:(function(o){function n(){}n.prototype={on:function(i,s,a){var c=this.e||(this.e={});return(c[i]||(c[i]=[])).push({fn:s,ctx:a}),this},once:function(i,s,a){var c=this;function p(){c.off(i,p),s.apply(a,arguments)}return p._=s,this.on(i,p,a)},emit:function(i){var s=[].slice.call(arguments,1),a=((this.e||(this.e={}))[i]||[]).slice(),c=0,p=a.length;for(c;c0&&i[i.length-1])&&(p[0]===6||p[0]===2)){r=0;continue}if(p[0]===3&&(!i||p[1]>i[0]&&p[1]=e.length&&(e=void 0),{value:e&&e[o++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function K(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var o=r.call(e),n,i=[],s;try{for(;(t===void 0||t-- >0)&&!(n=o.next()).done;)i.push(n.value)}catch(a){s={error:a}}finally{try{n&&!n.done&&(r=o.return)&&r.call(o)}finally{if(s)throw s.error}}return i}function B(e,t,r){if(r||arguments.length===2)for(var o=0,n=t.length,i;o1||c(d,S)})},v&&(n[d]=v(n[d])))}function c(d,v){try{p(o[d](v))}catch(S){u(i[0][3],S)}}function p(d){d.value instanceof dt?Promise.resolve(d.value.v).then(l,f):u(i[0][2],d)}function l(d){c("next",d)}function f(d){c("throw",d)}function u(d,v){d(v),i.shift(),i.length&&c(i[0][0],i[0][1])}}function To(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var t=e[Symbol.asyncIterator],r;return t?t.call(e):(e=typeof Oe=="function"?Oe(e):e[Symbol.iterator](),r={},o("next"),o("throw"),o("return"),r[Symbol.asyncIterator]=function(){return this},r);function o(i){r[i]=e[i]&&function(s){return new Promise(function(a,c){s=e[i](s),n(a,c,s.done,s.value)})}}function n(i,s,a,c){Promise.resolve(c).then(function(p){i({value:p,done:a})},s)}}function I(e){return typeof e=="function"}function yt(e){var t=function(o){Error.call(o),o.stack=new Error().stack},r=e(t);return r.prototype=Object.create(Error.prototype),r.prototype.constructor=r,r}var Jt=yt(function(e){return function(r){e(this),this.message=r?r.length+` errors occurred during unsubscription: +`+r.map(function(o,n){return n+1+") "+o.toString()}).join(` + `):"",this.name="UnsubscriptionError",this.errors=r}});function Ze(e,t){if(e){var r=e.indexOf(t);0<=r&&e.splice(r,1)}}var qe=(function(){function e(t){this.initialTeardown=t,this.closed=!1,this._parentage=null,this._finalizers=null}return e.prototype.unsubscribe=function(){var t,r,o,n,i;if(!this.closed){this.closed=!0;var s=this._parentage;if(s)if(this._parentage=null,Array.isArray(s))try{for(var a=Oe(s),c=a.next();!c.done;c=a.next()){var p=c.value;p.remove(this)}}catch(S){t={error:S}}finally{try{c&&!c.done&&(r=a.return)&&r.call(a)}finally{if(t)throw t.error}}else s.remove(this);var l=this.initialTeardown;if(I(l))try{l()}catch(S){i=S instanceof Jt?S.errors:[S]}var f=this._finalizers;if(f){this._finalizers=null;try{for(var u=Oe(f),d=u.next();!d.done;d=u.next()){var v=d.value;try{So(v)}catch(S){i=i!=null?i:[],S instanceof Jt?i=B(B([],K(i)),K(S.errors)):i.push(S)}}}catch(S){o={error:S}}finally{try{d&&!d.done&&(n=u.return)&&n.call(u)}finally{if(o)throw o.error}}}if(i)throw new Jt(i)}},e.prototype.add=function(t){var r;if(t&&t!==this)if(this.closed)So(t);else{if(t instanceof e){if(t.closed||t._hasParent(this))return;t._addParent(this)}(this._finalizers=(r=this._finalizers)!==null&&r!==void 0?r:[]).push(t)}},e.prototype._hasParent=function(t){var r=this._parentage;return r===t||Array.isArray(r)&&r.includes(t)},e.prototype._addParent=function(t){var r=this._parentage;this._parentage=Array.isArray(r)?(r.push(t),r):r?[r,t]:t},e.prototype._removeParent=function(t){var r=this._parentage;r===t?this._parentage=null:Array.isArray(r)&&Ze(r,t)},e.prototype.remove=function(t){var r=this._finalizers;r&&Ze(r,t),t instanceof e&&t._removeParent(this)},e.EMPTY=(function(){var t=new e;return t.closed=!0,t})(),e})();var $r=qe.EMPTY;function Xt(e){return e instanceof qe||e&&"closed"in e&&I(e.remove)&&I(e.add)&&I(e.unsubscribe)}function So(e){I(e)?e():e.unsubscribe()}var De={onUnhandledError:null,onStoppedNotification:null,Promise:void 0,useDeprecatedSynchronousErrorHandling:!1,useDeprecatedNextContext:!1};var xt={setTimeout:function(e,t){for(var r=[],o=2;o0},enumerable:!1,configurable:!0}),t.prototype._trySubscribe=function(r){return this._throwIfClosed(),e.prototype._trySubscribe.call(this,r)},t.prototype._subscribe=function(r){return this._throwIfClosed(),this._checkFinalizedStatuses(r),this._innerSubscribe(r)},t.prototype._innerSubscribe=function(r){var o=this,n=this,i=n.hasError,s=n.isStopped,a=n.observers;return i||s?$r:(this.currentObservers=null,a.push(r),new qe(function(){o.currentObservers=null,Ze(a,r)}))},t.prototype._checkFinalizedStatuses=function(r){var o=this,n=o.hasError,i=o.thrownError,s=o.isStopped;n?r.error(i):s&&r.complete()},t.prototype.asObservable=function(){var r=new F;return r.source=this,r},t.create=function(r,o){return new Ho(r,o)},t})(F);var Ho=(function(e){ie(t,e);function t(r,o){var n=e.call(this)||this;return n.destination=r,n.source=o,n}return t.prototype.next=function(r){var o,n;(n=(o=this.destination)===null||o===void 0?void 0:o.next)===null||n===void 0||n.call(o,r)},t.prototype.error=function(r){var o,n;(n=(o=this.destination)===null||o===void 0?void 0:o.error)===null||n===void 0||n.call(o,r)},t.prototype.complete=function(){var r,o;(o=(r=this.destination)===null||r===void 0?void 0:r.complete)===null||o===void 0||o.call(r)},t.prototype._subscribe=function(r){var o,n;return(n=(o=this.source)===null||o===void 0?void 0:o.subscribe(r))!==null&&n!==void 0?n:$r},t})(T);var jr=(function(e){ie(t,e);function t(r){var o=e.call(this)||this;return o._value=r,o}return Object.defineProperty(t.prototype,"value",{get:function(){return this.getValue()},enumerable:!1,configurable:!0}),t.prototype._subscribe=function(r){var o=e.prototype._subscribe.call(this,r);return!o.closed&&r.next(this._value),o},t.prototype.getValue=function(){var r=this,o=r.hasError,n=r.thrownError,i=r._value;if(o)throw n;return this._throwIfClosed(),i},t.prototype.next=function(r){e.prototype.next.call(this,this._value=r)},t})(T);var Rt={now:function(){return(Rt.delegate||Date).now()},delegate:void 0};var It=(function(e){ie(t,e);function t(r,o,n){r===void 0&&(r=1/0),o===void 0&&(o=1/0),n===void 0&&(n=Rt);var i=e.call(this)||this;return i._bufferSize=r,i._windowTime=o,i._timestampProvider=n,i._buffer=[],i._infiniteTimeWindow=!0,i._infiniteTimeWindow=o===1/0,i._bufferSize=Math.max(1,r),i._windowTime=Math.max(1,o),i}return t.prototype.next=function(r){var o=this,n=o.isStopped,i=o._buffer,s=o._infiniteTimeWindow,a=o._timestampProvider,c=o._windowTime;n||(i.push(r),!s&&i.push(a.now()+c)),this._trimBuffer(),e.prototype.next.call(this,r)},t.prototype._subscribe=function(r){this._throwIfClosed(),this._trimBuffer();for(var o=this._innerSubscribe(r),n=this,i=n._infiniteTimeWindow,s=n._buffer,a=s.slice(),c=0;c0?e.prototype.schedule.call(this,r,o):(this.delay=o,this.state=r,this.scheduler.flush(this),this)},t.prototype.execute=function(r,o){return o>0||this.closed?e.prototype.execute.call(this,r,o):this._execute(r,o)},t.prototype.requestAsyncId=function(r,o,n){return n===void 0&&(n=0),n!=null&&n>0||n==null&&this.delay>0?e.prototype.requestAsyncId.call(this,r,o,n):(r.flush(this),0)},t})(St);var Ro=(function(e){ie(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t})(Ot);var Dr=new Ro(Po);var Io=(function(e){ie(t,e);function t(r,o){var n=e.call(this,r,o)||this;return n.scheduler=r,n.work=o,n}return t.prototype.requestAsyncId=function(r,o,n){return n===void 0&&(n=0),n!==null&&n>0?e.prototype.requestAsyncId.call(this,r,o,n):(r.actions.push(this),r._scheduled||(r._scheduled=Tt.requestAnimationFrame(function(){return r.flush(void 0)})))},t.prototype.recycleAsyncId=function(r,o,n){var i;if(n===void 0&&(n=0),n!=null?n>0:this.delay>0)return e.prototype.recycleAsyncId.call(this,r,o,n);var s=r.actions;o!=null&&o===r._scheduled&&((i=s[s.length-1])===null||i===void 0?void 0:i.id)!==o&&(Tt.cancelAnimationFrame(o),r._scheduled=void 0)},t})(St);var Fo=(function(e){ie(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t.prototype.flush=function(r){this._active=!0;var o;r?o=r.id:(o=this._scheduled,this._scheduled=void 0);var n=this.actions,i;r=r||n.shift();do if(i=r.execute(r.state,r.delay))break;while((r=n[0])&&r.id===o&&n.shift());if(this._active=!1,i){for(;(r=n[0])&&r.id===o&&n.shift();)r.unsubscribe();throw i}},t})(Ot);var ye=new Fo(Io);var y=new F(function(e){return e.complete()});function tr(e){return e&&I(e.schedule)}function Vr(e){return e[e.length-1]}function pt(e){return I(Vr(e))?e.pop():void 0}function Fe(e){return tr(Vr(e))?e.pop():void 0}function rr(e,t){return typeof Vr(e)=="number"?e.pop():t}var Lt=(function(e){return e&&typeof e.length=="number"&&typeof e!="function"});function or(e){return I(e==null?void 0:e.then)}function nr(e){return I(e[wt])}function ir(e){return Symbol.asyncIterator&&I(e==null?void 0:e[Symbol.asyncIterator])}function ar(e){return new TypeError("You provided "+(e!==null&&typeof e=="object"?"an invalid object":"'"+e+"'")+" where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.")}function fa(){return typeof Symbol!="function"||!Symbol.iterator?"@@iterator":Symbol.iterator}var sr=fa();function cr(e){return I(e==null?void 0:e[sr])}function pr(e){return wo(this,arguments,function(){var r,o,n,i;return Gt(this,function(s){switch(s.label){case 0:r=e.getReader(),s.label=1;case 1:s.trys.push([1,,9,10]),s.label=2;case 2:return[4,dt(r.read())];case 3:return o=s.sent(),n=o.value,i=o.done,i?[4,dt(void 0)]:[3,5];case 4:return[2,s.sent()];case 5:return[4,dt(n)];case 6:return[4,s.sent()];case 7:return s.sent(),[3,2];case 8:return[3,10];case 9:return r.releaseLock(),[7];case 10:return[2]}})})}function lr(e){return I(e==null?void 0:e.getReader)}function U(e){if(e instanceof F)return e;if(e!=null){if(nr(e))return ua(e);if(Lt(e))return da(e);if(or(e))return ha(e);if(ir(e))return jo(e);if(cr(e))return ba(e);if(lr(e))return va(e)}throw ar(e)}function ua(e){return new F(function(t){var r=e[wt]();if(I(r.subscribe))return r.subscribe(t);throw new TypeError("Provided object does not correctly implement Symbol.observable")})}function da(e){return new F(function(t){for(var r=0;r=2;return function(o){return o.pipe(e?g(function(n,i){return e(n,i,o)}):be,Ee(1),r?Qe(t):tn(function(){return new fr}))}}function Yr(e){return e<=0?function(){return y}:E(function(t,r){var o=[];t.subscribe(w(r,function(n){o.push(n),e=2,!0))}function le(e){e===void 0&&(e={});var t=e.connector,r=t===void 0?function(){return new T}:t,o=e.resetOnError,n=o===void 0?!0:o,i=e.resetOnComplete,s=i===void 0?!0:i,a=e.resetOnRefCountZero,c=a===void 0?!0:a;return function(p){var l,f,u,d=0,v=!1,S=!1,X=function(){f==null||f.unsubscribe(),f=void 0},re=function(){X(),l=u=void 0,v=S=!1},ee=function(){var k=l;re(),k==null||k.unsubscribe()};return E(function(k,ut){d++,!S&&!v&&X();var je=u=u!=null?u:r();ut.add(function(){d--,d===0&&!S&&!v&&(f=Br(ee,c))}),je.subscribe(ut),!l&&d>0&&(l=new bt({next:function(R){return je.next(R)},error:function(R){S=!0,X(),f=Br(re,n,R),je.error(R)},complete:function(){v=!0,X(),f=Br(re,s),je.complete()}}),U(k).subscribe(l))})(p)}}function Br(e,t){for(var r=[],o=2;oe.next(document)),e}function M(e,t=document){return Array.from(t.querySelectorAll(e))}function j(e,t=document){let r=ue(e,t);if(typeof r=="undefined")throw new ReferenceError(`Missing element: expected "${e}" to be present`);return r}function ue(e,t=document){return t.querySelector(e)||void 0}function Ne(){var e,t,r,o;return(o=(r=(t=(e=document.activeElement)==null?void 0:e.shadowRoot)==null?void 0:t.activeElement)!=null?r:document.activeElement)!=null?o:void 0}var Ra=L(h(document.body,"focusin"),h(document.body,"focusout")).pipe(Ae(1),Q(void 0),m(()=>Ne()||document.body),Z(1));function Ye(e){return Ra.pipe(m(t=>e.contains(t)),Y())}function it(e,t){return H(()=>L(h(e,"mouseenter").pipe(m(()=>!0)),h(e,"mouseleave").pipe(m(()=>!1))).pipe(t?jt(r=>He(+!r*t)):be,Q(e.matches(":hover"))))}function sn(e,t){if(typeof t=="string"||typeof t=="number")e.innerHTML+=t.toString();else if(t instanceof Node)e.appendChild(t);else if(Array.isArray(t))for(let r of t)sn(e,r)}function x(e,t,...r){let o=document.createElement(e);if(t)for(let n of Object.keys(t))typeof t[n]!="undefined"&&(typeof t[n]!="boolean"?o.setAttribute(n,t[n]):o.setAttribute(n,""));for(let n of r)sn(o,n);return o}function br(e){if(e>999){let t=+((e-950)%1e3>99);return`${((e+1e-6)/1e3).toFixed(t)}k`}else return e.toString()}function _t(e){let t=x("script",{src:e});return H(()=>(document.head.appendChild(t),L(h(t,"load"),h(t,"error").pipe(b(()=>Nr(()=>new ReferenceError(`Invalid script: ${e}`))))).pipe(m(()=>{}),A(()=>document.head.removeChild(t)),Ee(1))))}var cn=new T,Ia=H(()=>typeof ResizeObserver=="undefined"?_t("https://unpkg.com/resize-observer-polyfill"):$(void 0)).pipe(m(()=>new ResizeObserver(e=>e.forEach(t=>cn.next(t)))),b(e=>L(tt,$(e)).pipe(A(()=>e.disconnect()))),Z(1));function de(e){return{width:e.offsetWidth,height:e.offsetHeight}}function Le(e){let t=e;for(;t.clientWidth===0&&t.parentElement;)t=t.parentElement;return Ia.pipe(O(r=>r.observe(t)),b(r=>cn.pipe(g(o=>o.target===t),A(()=>r.unobserve(t)))),m(()=>de(e)),Q(de(e)))}function At(e){return{width:e.scrollWidth,height:e.scrollHeight}}function vr(e){let t=e.parentElement;for(;t&&(e.scrollWidth<=t.scrollWidth&&e.scrollHeight<=t.scrollHeight);)t=(e=t).parentElement;return t?e:void 0}function pn(e){let t=[],r=e.parentElement;for(;r;)(e.clientWidth>r.clientWidth||e.clientHeight>r.clientHeight)&&t.push(r),r=(e=r).parentElement;return t.length===0&&t.push(document.documentElement),t}function Be(e){return{x:e.offsetLeft,y:e.offsetTop}}function ln(e){let t=e.getBoundingClientRect();return{x:t.x+window.scrollX,y:t.y+window.scrollY}}function mn(e){return L(h(window,"load"),h(window,"resize")).pipe($e(0,ye),m(()=>Be(e)),Q(Be(e)))}function gr(e){return{x:e.scrollLeft,y:e.scrollTop}}function Ge(e){return L(h(e,"scroll"),h(window,"scroll"),h(window,"resize")).pipe($e(0,ye),m(()=>gr(e)),Q(gr(e)))}var fn=new T,Fa=H(()=>$(new IntersectionObserver(e=>{for(let t of e)fn.next(t)},{threshold:0}))).pipe(b(e=>L(tt,$(e)).pipe(A(()=>e.disconnect()))),Z(1));function mt(e){return Fa.pipe(O(t=>t.observe(e)),b(t=>fn.pipe(g(({target:r})=>r===e),A(()=>t.unobserve(e)),m(({isIntersecting:r})=>r))))}function un(e,t=16){return Ge(e).pipe(m(({y:r})=>{let o=de(e),n=At(e);return r>=n.height-o.height-t}),Y())}var yr={drawer:j("[data-md-toggle=drawer]"),search:j("[data-md-toggle=search]")};function dn(e){return yr[e].checked}function at(e,t){yr[e].checked!==t&&yr[e].click()}function Je(e){let t=yr[e];return h(t,"change").pipe(m(()=>t.checked),Q(t.checked))}function ja(e,t){switch(e.constructor){case HTMLInputElement:return e.type==="radio"?/^Arrow/.test(t):!0;case HTMLSelectElement:case HTMLTextAreaElement:return!0;default:return e.isContentEditable}}function Ua(){return L(h(window,"compositionstart").pipe(m(()=>!0)),h(window,"compositionend").pipe(m(()=>!1))).pipe(Q(!1))}function hn(){let e=h(window,"keydown").pipe(g(t=>!(t.metaKey||t.ctrlKey)),m(t=>({mode:dn("search")?"search":"global",type:t.key,claim(){t.preventDefault(),t.stopPropagation()}})),g(({mode:t,type:r})=>{if(t==="global"){let o=Ne();if(typeof o!="undefined")return!ja(o,r)}return!0}),le());return Ua().pipe(b(t=>t?y:e))}function we(){return new URL(location.href)}function st(e,t=!1){if(V("navigation.instant")&&!t){let r=x("a",{href:e.href});document.body.appendChild(r),r.click(),r.remove()}else location.href=e.href}function bn(){return new T}function vn(){return location.hash.slice(1)}function gn(e){let t=x("a",{href:e});t.addEventListener("click",r=>r.stopPropagation()),t.click()}function Zr(e){return L(h(window,"hashchange"),e).pipe(m(vn),Q(vn()),g(t=>t.length>0),Z(1))}function yn(e){return Zr(e).pipe(m(t=>ue(`[id="${t}"]`)),g(t=>typeof t!="undefined"))}function Wt(e){let t=matchMedia(e);return ur(r=>t.addListener(()=>r(t.matches))).pipe(Q(t.matches))}function xn(){let e=matchMedia("print");return L(h(window,"beforeprint").pipe(m(()=>!0)),h(window,"afterprint").pipe(m(()=>!1))).pipe(Q(e.matches))}function eo(e,t){return e.pipe(b(r=>r?t():y))}function to(e,t){return new F(r=>{let o=new XMLHttpRequest;return o.open("GET",`${e}`),o.responseType="blob",o.addEventListener("load",()=>{o.status>=200&&o.status<300?(r.next(o.response),r.complete()):r.error(new Error(o.statusText))}),o.addEventListener("error",()=>{r.error(new Error("Network error"))}),o.addEventListener("abort",()=>{r.complete()}),typeof(t==null?void 0:t.progress$)!="undefined"&&(o.addEventListener("progress",n=>{var i;if(n.lengthComputable)t.progress$.next(n.loaded/n.total*100);else{let s=(i=o.getResponseHeader("Content-Length"))!=null?i:0;t.progress$.next(n.loaded/+s*100)}}),t.progress$.next(5)),o.send(),()=>o.abort()})}function ze(e,t){return to(e,t).pipe(b(r=>r.text()),m(r=>JSON.parse(r)),Z(1))}function xr(e,t){let r=new DOMParser;return to(e,t).pipe(b(o=>o.text()),m(o=>r.parseFromString(o,"text/html")),Z(1))}function En(e,t){let r=new DOMParser;return to(e,t).pipe(b(o=>o.text()),m(o=>r.parseFromString(o,"text/xml")),Z(1))}function wn(){return{x:Math.max(0,scrollX),y:Math.max(0,scrollY)}}function Tn(){return L(h(window,"scroll",{passive:!0}),h(window,"resize",{passive:!0})).pipe(m(wn),Q(wn()))}function Sn(){return{width:innerWidth,height:innerHeight}}function On(){return h(window,"resize",{passive:!0}).pipe(m(Sn),Q(Sn()))}function Ln(){return z([Tn(),On()]).pipe(m(([e,t])=>({offset:e,size:t})),Z(1))}function Er(e,{viewport$:t,header$:r}){let o=t.pipe(ne("size")),n=z([o,r]).pipe(m(()=>Be(e)));return z([r,t,n]).pipe(m(([{height:i},{offset:s,size:a},{x:c,y:p}])=>({offset:{x:s.x-c,y:s.y-p+i},size:a})))}function Wa(e){return h(e,"message",t=>t.data)}function Da(e){let t=new T;return t.subscribe(r=>e.postMessage(r)),t}function Mn(e,t=new Worker(e)){let r=Wa(t),o=Da(t),n=new T;n.subscribe(o);let i=o.pipe(oe(),ae(!0));return n.pipe(oe(),Ve(r.pipe(W(i))),le())}var Va=j("#__config"),Ct=JSON.parse(Va.textContent);Ct.base=`${new URL(Ct.base,we())}`;function Te(){return Ct}function V(e){return Ct.features.includes(e)}function Me(e,t){return typeof t!="undefined"?Ct.translations[e].replace("#",t.toString()):Ct.translations[e]}function Ce(e,t=document){return j(`[data-md-component=${e}]`,t)}function me(e,t=document){return M(`[data-md-component=${e}]`,t)}function Na(e){let t=j(".md-typeset > :first-child",e);return h(t,"click",{once:!0}).pipe(m(()=>j(".md-typeset",e)),m(r=>({hash:__md_hash(r.innerHTML)})))}function _n(e){if(!V("announce.dismiss")||!e.childElementCount)return y;if(!e.hidden){let t=j(".md-typeset",e);__md_hash(t.innerHTML)===__md_get("__announce")&&(e.hidden=!0)}return H(()=>{let t=new T;return t.subscribe(({hash:r})=>{e.hidden=!0,__md_set("__announce",r)}),Na(e).pipe(O(r=>t.next(r)),A(()=>t.complete()),m(r=>P({ref:e},r)))})}function za(e,{target$:t}){return t.pipe(m(r=>({hidden:r!==e})))}function An(e,t){let r=new T;return r.subscribe(({hidden:o})=>{e.hidden=o}),za(e,t).pipe(O(o=>r.next(o)),A(()=>r.complete()),m(o=>P({ref:e},o)))}function Dt(e,t){return t==="inline"?x("div",{class:"md-tooltip md-tooltip--inline",id:e,role:"tooltip"},x("div",{class:"md-tooltip__inner md-typeset"})):x("div",{class:"md-tooltip",id:e,role:"tooltip"},x("div",{class:"md-tooltip__inner md-typeset"}))}function wr(...e){return x("div",{class:"md-tooltip2",role:"dialog"},x("div",{class:"md-tooltip2__inner md-typeset"},e))}function Cn(...e){return x("div",{class:"md-tooltip2",role:"tooltip"},x("div",{class:"md-tooltip2__inner md-typeset"},e))}function kn(e,t){if(t=t?`${t}_annotation_${e}`:void 0,t){let r=t?`#${t}`:void 0;return x("aside",{class:"md-annotation",tabIndex:0},Dt(t),x("a",{href:r,class:"md-annotation__index",tabIndex:-1},x("span",{"data-md-annotation-id":e})))}else return x("aside",{class:"md-annotation",tabIndex:0},Dt(t),x("span",{class:"md-annotation__index",tabIndex:-1},x("span",{"data-md-annotation-id":e})))}function Hn(e){return x("button",{class:"md-code__button",title:Me("clipboard.copy"),"data-clipboard-target":`#${e} > code`,"data-md-type":"copy"})}function $n(){return x("button",{class:"md-code__button",title:"Toggle line selection","data-md-type":"select"})}function Pn(){return x("nav",{class:"md-code__nav"})}var In=$t(ro());function oo(e,t){let r=t&2,o=t&1,n=Object.keys(e.terms).filter(c=>!e.terms[c]).reduce((c,p)=>[...c,x("del",null,(0,In.default)(p))," "],[]).slice(0,-1),i=Te(),s=new URL(e.location,i.base);V("search.highlight")&&s.searchParams.set("h",Object.entries(e.terms).filter(([,c])=>c).reduce((c,[p])=>`${c} ${p}`.trim(),""));let{tags:a}=Te();return x("a",{href:`${s}`,class:"md-search-result__link",tabIndex:-1},x("article",{class:"md-search-result__article md-typeset","data-md-score":e.score.toFixed(2)},r>0&&x("div",{class:"md-search-result__icon md-icon"}),r>0&&x("h1",null,e.title),r<=0&&x("h2",null,e.title),o>0&&e.text.length>0&&e.text,e.tags&&x("nav",{class:"md-tags"},e.tags.map(c=>{let p=a?c in a?`md-tag-icon md-tag--${a[c]}`:"md-tag-icon":"";return x("span",{class:`md-tag ${p}`},c)})),o>0&&n.length>0&&x("p",{class:"md-search-result__terms"},Me("search.result.term.missing"),": ",...n)))}function Fn(e){let t=e[0].score,r=[...e],o=Te(),n=r.findIndex(l=>!`${new URL(l.location,o.base)}`.includes("#")),[i]=r.splice(n,1),s=r.findIndex(l=>l.scoreoo(l,1)),...c.length?[x("details",{class:"md-search-result__more"},x("summary",{tabIndex:-1},x("div",null,c.length>0&&c.length===1?Me("search.result.more.one"):Me("search.result.more.other",c.length))),...c.map(l=>oo(l,1)))]:[]];return x("li",{class:"md-search-result__item"},p)}function jn(e){return x("ul",{class:"md-source__facts"},Object.entries(e).map(([t,r])=>x("li",{class:`md-source__fact md-source__fact--${t}`},typeof r=="number"?br(r):r)))}function no(e){let t=`tabbed-control tabbed-control--${e}`;return x("div",{class:t,hidden:!0},x("button",{class:"tabbed-button",tabIndex:-1,"aria-hidden":"true"}))}function Un(e){return x("div",{class:"md-typeset__scrollwrap"},x("div",{class:"md-typeset__table"},e))}function Qa(e){var o;let t=Te(),r=new URL(`../${e.version}/`,t.base);return x("li",{class:"md-version__item"},x("a",{href:`${r}`,class:"md-version__link"},e.title,((o=t.version)==null?void 0:o.alias)&&e.aliases.length>0&&x("span",{class:"md-version__alias"},e.aliases[0])))}function Wn(e,t){var o;let r=Te();return e=e.filter(n=>{var i;return!((i=n.properties)!=null&&i.hidden)}),x("div",{class:"md-version"},x("button",{class:"md-version__current","aria-label":Me("select.version")},t.title,((o=r.version)==null?void 0:o.alias)&&t.aliases.length>0&&x("span",{class:"md-version__alias"},t.aliases[0])),x("ul",{class:"md-version__list"},e.map(Qa)))}var Ya=0;function Ba(e,t=250){let r=z([Ye(e),it(e,t)]).pipe(m(([n,i])=>n||i),Y()),o=H(()=>pn(e)).pipe(J(Ge),gt(1),Pe(r),m(()=>ln(e)));return r.pipe(Re(n=>n),b(()=>z([r,o])),m(([n,i])=>({active:n,offset:i})),le())}function Vt(e,t,r=250){let{content$:o,viewport$:n}=t,i=`__tooltip2_${Ya++}`;return H(()=>{let s=new T,a=new jr(!1);s.pipe(oe(),ae(!1)).subscribe(a);let c=a.pipe(jt(l=>He(+!l*250,Dr)),Y(),b(l=>l?o:y),O(l=>l.id=i),le());z([s.pipe(m(({active:l})=>l)),c.pipe(b(l=>it(l,250)),Q(!1))]).pipe(m(l=>l.some(f=>f))).subscribe(a);let p=a.pipe(g(l=>l),te(c,n),m(([l,f,{size:u}])=>{let d=e.getBoundingClientRect(),v=d.width/2;if(f.role==="tooltip")return{x:v,y:8+d.height};if(d.y>=u.height/2){let{height:S}=de(f);return{x:v,y:-16-S}}else return{x:v,y:16+d.height}}));return z([c,s,p]).subscribe(([l,{offset:f},u])=>{l.style.setProperty("--md-tooltip-host-x",`${f.x}px`),l.style.setProperty("--md-tooltip-host-y",`${f.y}px`),l.style.setProperty("--md-tooltip-x",`${u.x}px`),l.style.setProperty("--md-tooltip-y",`${u.y}px`),l.classList.toggle("md-tooltip2--top",u.y<0),l.classList.toggle("md-tooltip2--bottom",u.y>=0)}),a.pipe(g(l=>l),te(c,(l,f)=>f),g(l=>l.role==="tooltip")).subscribe(l=>{let f=de(j(":scope > *",l));l.style.setProperty("--md-tooltip-width",`${f.width}px`),l.style.setProperty("--md-tooltip-tail","0px")}),a.pipe(Y(),xe(ye),te(c)).subscribe(([l,f])=>{f.classList.toggle("md-tooltip2--active",l)}),z([a.pipe(g(l=>l)),c]).subscribe(([l,f])=>{f.role==="dialog"?(e.setAttribute("aria-controls",i),e.setAttribute("aria-haspopup","dialog")):e.setAttribute("aria-describedby",i)}),a.pipe(g(l=>!l)).subscribe(()=>{e.removeAttribute("aria-controls"),e.removeAttribute("aria-describedby"),e.removeAttribute("aria-haspopup")}),Ba(e,r).pipe(O(l=>s.next(l)),A(()=>s.complete()),m(l=>P({ref:e},l)))})}function Xe(e,{viewport$:t},r=document.body){return Vt(e,{content$:new F(o=>{let n=e.title,i=Cn(n);return o.next(i),e.removeAttribute("title"),r.append(i),()=>{i.remove(),e.setAttribute("title",n)}}),viewport$:t},0)}function Ga(e,t){let r=H(()=>z([mn(e),Ge(t)])).pipe(m(([{x:o,y:n},i])=>{let{width:s,height:a}=de(e);return{x:o-i.x+s/2,y:n-i.y+a/2}}));return Ye(e).pipe(b(o=>r.pipe(m(n=>({active:o,offset:n})),Ee(+!o||1/0))))}function Dn(e,t,{target$:r}){let[o,n]=Array.from(e.children);return H(()=>{let i=new T,s=i.pipe(oe(),ae(!0));return i.subscribe({next({offset:a}){e.style.setProperty("--md-tooltip-x",`${a.x}px`),e.style.setProperty("--md-tooltip-y",`${a.y}px`)},complete(){e.style.removeProperty("--md-tooltip-x"),e.style.removeProperty("--md-tooltip-y")}}),mt(e).pipe(W(s)).subscribe(a=>{e.toggleAttribute("data-md-visible",a)}),L(i.pipe(g(({active:a})=>a)),i.pipe(Ae(250),g(({active:a})=>!a))).subscribe({next({active:a}){a?e.prepend(o):o.remove()},complete(){e.prepend(o)}}),i.pipe($e(16,ye)).subscribe(({active:a})=>{o.classList.toggle("md-tooltip--active",a)}),i.pipe(gt(125,ye),g(()=>!!e.offsetParent),m(()=>e.offsetParent.getBoundingClientRect()),m(({x:a})=>a)).subscribe({next(a){a?e.style.setProperty("--md-tooltip-0",`${-a}px`):e.style.removeProperty("--md-tooltip-0")},complete(){e.style.removeProperty("--md-tooltip-0")}}),h(n,"click").pipe(W(s),g(a=>!(a.metaKey||a.ctrlKey))).subscribe(a=>{a.stopPropagation(),a.preventDefault()}),h(n,"mousedown").pipe(W(s),te(i)).subscribe(([a,{active:c}])=>{var p;if(a.button!==0||a.metaKey||a.ctrlKey)a.preventDefault();else if(c){a.preventDefault();let l=e.parentElement.closest(".md-annotation");l instanceof HTMLElement?l.focus():(p=Ne())==null||p.blur()}}),r.pipe(W(s),g(a=>a===o),nt(125)).subscribe(()=>e.focus()),Ga(e,t).pipe(O(a=>i.next(a)),A(()=>i.complete()),m(a=>P({ref:e},a)))})}function Ja(e){let t=Te();if(e.tagName!=="CODE")return[e];let r=[".c",".c1",".cm"];if(t.annotate&&typeof t.annotate=="object"){let o=e.closest("[class|=language]");if(o)for(let n of Array.from(o.classList)){if(!n.startsWith("language-"))continue;let[,i]=n.split("-");i in t.annotate&&r.push(...t.annotate[i])}}return M(r.join(", "),e)}function Xa(e){let t=[];for(let r of Ja(e)){let o=[],n=document.createNodeIterator(r,NodeFilter.SHOW_TEXT);for(let i=n.nextNode();i;i=n.nextNode())o.push(i);for(let i of o){let s;for(;s=/(\(\d+\))(!)?/.exec(i.textContent);){let[,a,c]=s;if(typeof c=="undefined"){let p=i.splitText(s.index);i=p.splitText(a.length),t.push(p)}else{i.textContent=a,t.push(i);break}}}}return t}function Vn(e,t){t.append(...Array.from(e.childNodes))}function Tr(e,t,{target$:r,print$:o}){let n=t.closest("[id]"),i=n==null?void 0:n.id,s=new Map;for(let a of Xa(t)){let[,c]=a.textContent.match(/\((\d+)\)/);ue(`:scope > li:nth-child(${c})`,e)&&(s.set(c,kn(c,i)),a.replaceWith(s.get(c)))}return s.size===0?y:H(()=>{let a=new T,c=a.pipe(oe(),ae(!0)),p=[];for(let[l,f]of s)p.push([j(".md-typeset",f),j(`:scope > li:nth-child(${l})`,e)]);return o.pipe(W(c)).subscribe(l=>{e.hidden=!l,e.classList.toggle("md-annotation-list",l);for(let[f,u]of p)l?Vn(f,u):Vn(u,f)}),L(...[...s].map(([,l])=>Dn(l,t,{target$:r}))).pipe(A(()=>a.complete()),le())})}function Nn(e){if(e.nextElementSibling){let t=e.nextElementSibling;if(t.tagName==="OL")return t;if(t.tagName==="P"&&!t.children.length)return Nn(t)}}function zn(e,t){return H(()=>{let r=Nn(e);return typeof r!="undefined"?Tr(r,e,t):y})}var Kn=$t(ao());var Za=0,qn=L(h(window,"keydown").pipe(m(()=>!0)),L(h(window,"keyup"),h(window,"contextmenu")).pipe(m(()=>!1))).pipe(Q(!1),Z(1));function Qn(e){if(e.nextElementSibling){let t=e.nextElementSibling;if(t.tagName==="OL")return t;if(t.tagName==="P"&&!t.children.length)return Qn(t)}}function es(e){return Le(e).pipe(m(({width:t})=>({scrollable:At(e).width>t})),ne("scrollable"))}function Yn(e,t){let{matches:r}=matchMedia("(hover)"),o=H(()=>{let n=new T,i=n.pipe(Yr(1));n.subscribe(({scrollable:d})=>{d&&r?e.setAttribute("tabindex","0"):e.removeAttribute("tabindex")});let s=[],a=e.closest("pre"),c=a.closest("[id]"),p=c?c.id:Za++;a.id=`__code_${p}`;let l=[],f=e.closest(".highlight");if(f instanceof HTMLElement){let d=Qn(f);if(typeof d!="undefined"&&(f.classList.contains("annotate")||V("content.code.annotate"))){let v=Tr(d,e,t);l.push(Le(f).pipe(W(i),m(({width:S,height:X})=>S&&X),Y(),b(S=>S?v:y)))}}let u=M(":scope > span[id]",e);if(u.length&&(e.classList.add("md-code__content"),e.closest(".select")||V("content.code.select")&&!e.closest(".no-select"))){let d=+u[0].id.split("-").pop(),v=$n();s.push(v),V("content.tooltips")&&l.push(Xe(v,{viewport$}));let S=h(v,"click").pipe(Ut(R=>!R,!1),O(()=>v.blur()),le());S.subscribe(R=>{v.classList.toggle("md-code__button--active",R)});let X=fe(u).pipe(J(R=>it(R).pipe(m(se=>[R,se]))));S.pipe(b(R=>R?X:y)).subscribe(([R,se])=>{let ce=ue(".hll.select",R);if(ce&&!se)ce.replaceWith(...Array.from(ce.childNodes));else if(!ce&&se){let he=document.createElement("span");he.className="hll select",he.append(...Array.from(R.childNodes).slice(1)),R.append(he)}});let re=fe(u).pipe(J(R=>h(R,"mousedown").pipe(O(se=>se.preventDefault()),m(()=>R)))),ee=S.pipe(b(R=>R?re:y),te(qn),m(([R,se])=>{var he;let ce=u.indexOf(R)+d;if(se===!1)return[ce,ce];{let Se=M(".hll",e).map(Ue=>u.indexOf(Ue.parentElement)+d);return(he=window.getSelection())==null||he.removeAllRanges(),[Math.min(ce,...Se),Math.max(ce,...Se)]}})),k=Zr(y).pipe(g(R=>R.startsWith(`__codelineno-${p}-`)));k.subscribe(R=>{let[,,se]=R.split("-"),ce=se.split(":").map(Se=>+Se-d+1);ce.length===1&&ce.push(ce[0]);for(let Se of M(".hll:not(.select)",e))Se.replaceWith(...Array.from(Se.childNodes));let he=u.slice(ce[0]-1,ce[1]);for(let Se of he){let Ue=document.createElement("span");Ue.className="hll",Ue.append(...Array.from(Se.childNodes).slice(1)),Se.append(Ue)}}),k.pipe(Ee(1),xe(pe)).subscribe(R=>{if(R.includes(":")){let se=document.getElementById(R.split(":")[0]);se&&setTimeout(()=>{let ce=se,he=-64;for(;ce!==document.body;)he+=ce.offsetTop,ce=ce.offsetParent;window.scrollTo({top:he})},1)}});let je=fe(M('a[href^="#__codelineno"]',f)).pipe(J(R=>h(R,"click").pipe(O(se=>se.preventDefault()),m(()=>R)))).pipe(W(i),te(qn),m(([R,se])=>{let he=+j(`[id="${R.hash.slice(1)}"]`).parentElement.id.split("-").pop();if(se===!1)return[he,he];{let Se=M(".hll",e).map(Ue=>+Ue.parentElement.id.split("-").pop());return[Math.min(he,...Se),Math.max(he,...Se)]}}));L(ee,je).subscribe(R=>{let se=`#__codelineno-${p}-`;R[0]===R[1]?se+=R[0]:se+=`${R[0]}:${R[1]}`,history.replaceState({},"",se),window.dispatchEvent(new HashChangeEvent("hashchange",{newURL:window.location.origin+window.location.pathname+se,oldURL:window.location.href}))})}if(Kn.default.isSupported()&&(e.closest(".copy")||V("content.code.copy")&&!e.closest(".no-copy"))){let d=Hn(a.id);s.push(d),V("content.tooltips")&&l.push(Xe(d,{viewport$}))}if(s.length){let d=Pn();d.append(...s),a.insertBefore(d,e)}return es(e).pipe(O(d=>n.next(d)),A(()=>n.complete()),m(d=>P({ref:e},d)),Ve(L(...l).pipe(W(i))))});return V("content.lazy")?mt(e).pipe(g(n=>n),Ee(1),b(()=>o)):o}function ts(e,{target$:t,print$:r}){let o=!0;return L(t.pipe(m(n=>n.closest("details:not([open])")),g(n=>e===n),m(()=>({action:"open",reveal:!0}))),r.pipe(g(n=>n||!o),O(()=>o=e.open),m(n=>({action:n?"open":"close"}))))}function Bn(e,t){return H(()=>{let r=new T;return r.subscribe(({action:o,reveal:n})=>{e.toggleAttribute("open",o==="open"),n&&e.scrollIntoView()}),ts(e,t).pipe(O(o=>r.next(o)),A(()=>r.complete()),m(o=>P({ref:e},o)))})}var Gn=0;function rs(e){let t=document.createElement("h3");t.innerHTML=e.innerHTML;let r=[t],o=e.nextElementSibling;for(;o&&!(o instanceof HTMLHeadingElement);)r.push(o),o=o.nextElementSibling;return r}function os(e,t){for(let r of M("[href], [src]",e))for(let o of["href","src"]){let n=r.getAttribute(o);if(n&&!/^(?:[a-z]+:)?\/\//i.test(n)){r[o]=new URL(r.getAttribute(o),t).toString();break}}for(let r of M("[name^=__], [for]",e))for(let o of["id","for","name"]){let n=r.getAttribute(o);n&&r.setAttribute(o,`${n}$preview_${Gn}`)}return Gn++,$(e)}function Jn(e,t){let{sitemap$:r}=t;if(!(e instanceof HTMLAnchorElement))return y;if(!(V("navigation.instant.preview")||e.hasAttribute("data-preview")))return y;e.removeAttribute("title");let o=z([Ye(e),it(e)]).pipe(m(([i,s])=>i||s),Y(),g(i=>i));return rt([r,o]).pipe(b(([i])=>{let s=new URL(e.href);return s.search=s.hash="",i.has(`${s}`)?$(s):y}),b(i=>xr(i).pipe(b(s=>os(s,i)))),b(i=>{let s=e.hash?`article [id="${e.hash.slice(1)}"]`:"article h1",a=ue(s,i);return typeof a=="undefined"?y:$(rs(a))})).pipe(b(i=>{let s=new F(a=>{let c=wr(...i);return a.next(c),document.body.append(c),()=>c.remove()});return Vt(e,P({content$:s},t))}))}var Xn=".node circle,.node ellipse,.node path,.node polygon,.node rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}marker{fill:var(--md-mermaid-edge-color)!important}.edgeLabel .label rect{fill:#0000}.flowchartTitleText{fill:var(--md-mermaid-label-fg-color)}.label{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.label foreignObject{line-height:normal;overflow:visible}.label div .edgeLabel{color:var(--md-mermaid-label-fg-color)}.edgeLabel,.edgeLabel p,.label div .edgeLabel{background-color:var(--md-mermaid-label-bg-color)}.edgeLabel,.edgeLabel p{fill:var(--md-mermaid-label-bg-color);color:var(--md-mermaid-edge-color)}.edgePath .path,.flowchart-link{stroke:var(--md-mermaid-edge-color)}.edgePath .arrowheadPath{fill:var(--md-mermaid-edge-color);stroke:none}.cluster rect{fill:var(--md-default-fg-color--lightest);stroke:var(--md-default-fg-color--lighter)}.cluster span{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}g #flowchart-circleEnd,g #flowchart-circleStart,g #flowchart-crossEnd,g #flowchart-crossStart,g #flowchart-pointEnd,g #flowchart-pointStart{stroke:none}.classDiagramTitleText{fill:var(--md-mermaid-label-fg-color)}g.classGroup line,g.classGroup rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}g.classGroup text{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.classLabel .box{fill:var(--md-mermaid-label-bg-color);background-color:var(--md-mermaid-label-bg-color);opacity:1}.classLabel .label{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.node .divider{stroke:var(--md-mermaid-node-fg-color)}.relation{stroke:var(--md-mermaid-edge-color)}.cardinality{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.cardinality text{fill:inherit!important}defs marker.marker.composition.class path,defs marker.marker.dependency.class path,defs marker.marker.extension.class path{fill:var(--md-mermaid-edge-color)!important;stroke:var(--md-mermaid-edge-color)!important}defs marker.marker.aggregation.class path{fill:var(--md-mermaid-label-bg-color)!important;stroke:var(--md-mermaid-edge-color)!important}.statediagramTitleText{fill:var(--md-mermaid-label-fg-color)}g.stateGroup rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}g.stateGroup .state-title{fill:var(--md-mermaid-label-fg-color)!important;font-family:var(--md-mermaid-font-family)}g.stateGroup .composit{fill:var(--md-mermaid-label-bg-color)}.nodeLabel,.nodeLabel p{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}a .nodeLabel{text-decoration:underline}.node circle.state-end,.node circle.state-start,.start-state{fill:var(--md-mermaid-edge-color);stroke:none}.end-state-inner,.end-state-outer{fill:var(--md-mermaid-edge-color)}.end-state-inner,.node circle.state-end{stroke:var(--md-mermaid-label-bg-color)}.transition{stroke:var(--md-mermaid-edge-color)}[id^=state-fork] rect,[id^=state-join] rect{fill:var(--md-mermaid-edge-color)!important;stroke:none!important}.statediagram-cluster.statediagram-cluster .inner{fill:var(--md-default-bg-color)}.statediagram-cluster rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}.statediagram-state rect.divider{fill:var(--md-default-fg-color--lightest);stroke:var(--md-default-fg-color--lighter)}defs #statediagram-barbEnd{stroke:var(--md-mermaid-edge-color)}[id^=entity] path,[id^=entity] rect{fill:var(--md-default-bg-color)}.relationshipLine{stroke:var(--md-mermaid-edge-color)}defs .marker.oneOrMore.er *,defs .marker.onlyOne.er *,defs .marker.zeroOrMore.er *,defs .marker.zeroOrOne.er *{stroke:var(--md-mermaid-edge-color)!important}text:not([class]):last-child{fill:var(--md-mermaid-label-fg-color)}.actor{fill:var(--md-mermaid-sequence-actor-bg-color);stroke:var(--md-mermaid-sequence-actor-border-color)}text.actor>tspan{fill:var(--md-mermaid-sequence-actor-fg-color);font-family:var(--md-mermaid-font-family)}line{stroke:var(--md-mermaid-sequence-actor-line-color)}.actor-man circle,.actor-man line{fill:var(--md-mermaid-sequence-actorman-bg-color);stroke:var(--md-mermaid-sequence-actorman-line-color)}.messageLine0,.messageLine1{stroke:var(--md-mermaid-sequence-message-line-color)}.note{fill:var(--md-mermaid-sequence-note-bg-color);stroke:var(--md-mermaid-sequence-note-border-color)}.loopText,.loopText>tspan,.messageText,.noteText>tspan{stroke:none;font-family:var(--md-mermaid-font-family)!important}.messageText{fill:var(--md-mermaid-sequence-message-fg-color)}.loopText,.loopText>tspan{fill:var(--md-mermaid-sequence-loop-fg-color)}.noteText>tspan{fill:var(--md-mermaid-sequence-note-fg-color)}#arrowhead path{fill:var(--md-mermaid-sequence-message-line-color);stroke:none}.loopLine{fill:var(--md-mermaid-sequence-loop-bg-color);stroke:var(--md-mermaid-sequence-loop-border-color)}.labelBox{fill:var(--md-mermaid-sequence-label-bg-color);stroke:none}.labelText,.labelText>span{fill:var(--md-mermaid-sequence-label-fg-color);font-family:var(--md-mermaid-font-family)}.sequenceNumber{fill:var(--md-mermaid-sequence-number-fg-color)}rect.rect{fill:var(--md-mermaid-sequence-box-bg-color);stroke:none}rect.rect+text.text{fill:var(--md-mermaid-sequence-box-fg-color)}defs #sequencenumber{fill:var(--md-mermaid-sequence-number-bg-color)!important}";var so,is=0;function as(){return typeof mermaid=="undefined"||mermaid instanceof Element?_t("https://unpkg.com/mermaid@11/dist/mermaid.min.js"):$(void 0)}function Zn(e){return e.classList.remove("mermaid"),so||(so=as().pipe(O(()=>mermaid.initialize({startOnLoad:!1,themeCSS:Xn,sequence:{actorFontSize:"16px",messageFontSize:"16px",noteFontSize:"16px"}})),m(()=>{}),Z(1))),so.subscribe(()=>go(null,null,function*(){e.classList.add("mermaid");let t=`__mermaid_${is++}`,r=x("div",{class:"mermaid"}),o=e.textContent,{svg:n,fn:i}=yield mermaid.render(t,o),s=r.attachShadow({mode:"closed"});s.innerHTML=n,e.replaceWith(r),i==null||i(s)})),so.pipe(m(()=>({ref:e})))}var ei=x("table");function ti(e){return e.replaceWith(ei),ei.replaceWith(Un(e)),$({ref:e})}function ss(e){let t=e.find(r=>r.checked)||e[0];return L(...e.map(r=>h(r,"change").pipe(m(()=>j(`label[for="${r.id}"]`))))).pipe(Q(j(`label[for="${t.id}"]`)),m(r=>({active:r})))}function ri(e,{viewport$:t,target$:r}){let o=j(".tabbed-labels",e),n=M(":scope > input",e),i=no("prev");e.append(i);let s=no("next");return e.append(s),H(()=>{let a=new T,c=a.pipe(oe(),ae(!0));z([a,Le(e),mt(e)]).pipe(W(c),$e(1,ye)).subscribe({next([{active:p},l]){let f=Be(p),{width:u}=de(p);e.style.setProperty("--md-indicator-x",`${f.x}px`),e.style.setProperty("--md-indicator-width",`${u}px`);let d=gr(o);(f.xd.x+l.width)&&o.scrollTo({left:Math.max(0,f.x-16),behavior:"smooth"})},complete(){e.style.removeProperty("--md-indicator-x"),e.style.removeProperty("--md-indicator-width")}}),z([Ge(o),Le(o)]).pipe(W(c)).subscribe(([p,l])=>{let f=At(o);i.hidden=p.x<16,s.hidden=p.x>f.width-l.width-16}),L(h(i,"click").pipe(m(()=>-1)),h(s,"click").pipe(m(()=>1))).pipe(W(c)).subscribe(p=>{let{width:l}=de(o);o.scrollBy({left:l*p,behavior:"smooth"})}),r.pipe(W(c),g(p=>n.includes(p))).subscribe(p=>p.click()),o.classList.add("tabbed-labels--linked");for(let p of n){let l=j(`label[for="${p.id}"]`);l.replaceChildren(x("a",{href:`#${l.htmlFor}`,tabIndex:-1},...Array.from(l.childNodes))),h(l.firstElementChild,"click").pipe(W(c),g(f=>!(f.metaKey||f.ctrlKey)),O(f=>{f.preventDefault(),f.stopPropagation()})).subscribe(()=>{history.replaceState({},"",`#${l.htmlFor}`),l.click()})}return V("content.tabs.link")&&a.pipe(Ie(1),te(t)).subscribe(([{active:p},{offset:l}])=>{let f=p.innerText.trim();if(p.hasAttribute("data-md-switching"))p.removeAttribute("data-md-switching");else{let u=e.offsetTop-l.y;for(let v of M("[data-tabs]"))for(let S of M(":scope > input",v)){let X=j(`label[for="${S.id}"]`);if(X!==p&&X.innerText.trim()===f){X.setAttribute("data-md-switching",""),S.click();break}}window.scrollTo({top:e.offsetTop-u});let d=__md_get("__tabs")||[];__md_set("__tabs",[...new Set([f,...d])])}}),a.pipe(W(c)).subscribe(()=>{for(let p of M("audio, video",e))p.offsetWidth&&p.autoplay?p.play().catch(()=>{}):p.pause()}),ss(n).pipe(O(p=>a.next(p)),A(()=>a.complete()),m(p=>P({ref:e},p)))}).pipe(et(pe))}function oi(e,t){let{viewport$:r,target$:o,print$:n}=t;return L(...M(".annotate:not(.highlight)",e).map(i=>zn(i,{target$:o,print$:n})),...M("pre:not(.mermaid) > code",e).map(i=>Yn(i,{target$:o,print$:n})),...M("a",e).map(i=>Jn(i,t)),...M("pre.mermaid",e).map(i=>Zn(i)),...M("table:not([class])",e).map(i=>ti(i)),...M("details",e).map(i=>Bn(i,{target$:o,print$:n})),...M("[data-tabs]",e).map(i=>ri(i,{viewport$:r,target$:o})),...M("[title]:not([data-preview])",e).filter(()=>V("content.tooltips")).map(i=>Xe(i,{viewport$:r})),...M(".footnote-ref",e).filter(()=>V("content.footnote.tooltips")).map(i=>Vt(i,{content$:new F(s=>{let a=new URL(i.href).hash.slice(1),c=Array.from(document.getElementById(a).cloneNode(!0).children),p=wr(...c);return s.next(p),document.body.append(p),()=>p.remove()}),viewport$:r})))}function cs(e,{alert$:t}){return t.pipe(b(r=>L($(!0),$(!1).pipe(nt(2e3))).pipe(m(o=>({message:r,active:o})))))}function ni(e,t){let r=j(".md-typeset",e);return H(()=>{let o=new T;return o.subscribe(({message:n,active:i})=>{e.classList.toggle("md-dialog--active",i),r.textContent=n}),cs(e,t).pipe(O(n=>o.next(n)),A(()=>o.complete()),m(n=>P({ref:e},n)))})}var ps=0;function ls(e,t){document.body.append(e);let{width:r}=de(e);e.style.setProperty("--md-tooltip-width",`${r}px`),e.remove();let o=vr(t),n=typeof o!="undefined"?Ge(o):$({x:0,y:0}),i=L(Ye(t),it(t)).pipe(Y());return z([i,n]).pipe(m(([s,a])=>{let{x:c,y:p}=Be(t),l=de(t),f=t.closest("table");return f&&t.parentElement&&(c+=f.offsetLeft+t.parentElement.offsetLeft,p+=f.offsetTop+t.parentElement.offsetTop),{active:s,offset:{x:c-a.x+l.width/2-r/2,y:p-a.y+l.height+8}}}))}function ii(e){let t=e.title;if(!t.length)return y;let r=`__tooltip_${ps++}`,o=Dt(r,"inline"),n=j(".md-typeset",o);return n.innerHTML=t,H(()=>{let i=new T;return i.subscribe({next({offset:s}){o.style.setProperty("--md-tooltip-x",`${s.x}px`),o.style.setProperty("--md-tooltip-y",`${s.y}px`)},complete(){o.style.removeProperty("--md-tooltip-x"),o.style.removeProperty("--md-tooltip-y")}}),L(i.pipe(g(({active:s})=>s)),i.pipe(Ae(250),g(({active:s})=>!s))).subscribe({next({active:s}){s?(e.insertAdjacentElement("afterend",o),e.setAttribute("aria-describedby",r),e.removeAttribute("title")):(o.remove(),e.removeAttribute("aria-describedby"),e.setAttribute("title",t))},complete(){o.remove(),e.removeAttribute("aria-describedby"),e.setAttribute("title",t)}}),i.pipe($e(16,ye)).subscribe(({active:s})=>{o.classList.toggle("md-tooltip--active",s)}),i.pipe(gt(125,ye),g(()=>!!e.offsetParent),m(()=>e.offsetParent.getBoundingClientRect()),m(({x:s})=>s)).subscribe({next(s){s?o.style.setProperty("--md-tooltip-0",`${-s}px`):o.style.removeProperty("--md-tooltip-0")},complete(){o.style.removeProperty("--md-tooltip-0")}}),ls(o,e).pipe(O(s=>i.next(s)),A(()=>i.complete()),m(s=>P({ref:e},s)))}).pipe(et(pe))}function ms({viewport$:e}){if(!V("header.autohide"))return $(!1);let t=e.pipe(m(({offset:{y:n}})=>n),ot(2,1),m(([n,i])=>[nMath.abs(i-n.y)>100),m(([,[n]])=>n),Y()),o=Je("search");return z([e,o]).pipe(m(([{offset:n},i])=>n.y>400&&!i),Y(),b(n=>n?r:$(!1)),Q(!1))}function ai(e,t){return H(()=>z([Le(e),ms(t)])).pipe(m(([{height:r},o])=>({height:r,hidden:o})),Y((r,o)=>r.height===o.height&&r.hidden===o.hidden),Z(1))}function si(e,{header$:t,main$:r}){return H(()=>{let o=new T,n=o.pipe(oe(),ae(!0));o.pipe(ne("active"),Pe(t)).subscribe(([{active:s},{hidden:a}])=>{e.classList.toggle("md-header--shadow",s&&!a),e.hidden=a});let i=fe(M("[title]",e)).pipe(g(()=>V("content.tooltips")),J(s=>ii(s)));return r.subscribe(o),t.pipe(W(n),m(s=>P({ref:e},s)),Ve(i.pipe(W(n))))})}function fs(e,{viewport$:t,header$:r}){return Er(e,{viewport$:t,header$:r}).pipe(m(({offset:{y:o}})=>{let{height:n}=de(e);return{active:n>0&&o>=n}}),ne("active"))}function ci(e,t){return H(()=>{let r=new T;r.subscribe({next({active:n}){e.classList.toggle("md-header__title--active",n)},complete(){e.classList.remove("md-header__title--active")}});let o=ue(".md-content h1");return typeof o=="undefined"?y:fs(o,t).pipe(O(n=>r.next(n)),A(()=>r.complete()),m(n=>P({ref:e},n)))})}function pi(e,{viewport$:t,header$:r}){let o=r.pipe(m(({height:i})=>i),Y()),n=o.pipe(b(()=>Le(e).pipe(m(({height:i})=>({top:e.offsetTop,bottom:e.offsetTop+i})),ne("bottom"))));return z([o,n,t]).pipe(m(([i,{top:s,bottom:a},{offset:{y:c},size:{height:p}}])=>(p=Math.max(0,p-Math.max(0,s-c,i)-Math.max(0,p+c-a)),{offset:s-i,height:p,active:s-i<=c})),Y((i,s)=>i.offset===s.offset&&i.height===s.height&&i.active===s.active))}function us(e){let t=__md_get("__palette")||{index:e.findIndex(o=>matchMedia(o.getAttribute("data-md-color-media")).matches)},r=Math.max(0,Math.min(t.index,e.length-1));return $(...e).pipe(J(o=>h(o,"change").pipe(m(()=>o))),Q(e[r]),m(o=>({index:e.indexOf(o),color:{media:o.getAttribute("data-md-color-media"),scheme:o.getAttribute("data-md-color-scheme"),primary:o.getAttribute("data-md-color-primary"),accent:o.getAttribute("data-md-color-accent")}})),Z(1))}function li(e){let t=M("input",e),r=x("meta",{name:"theme-color"});document.head.appendChild(r);let o=x("meta",{name:"color-scheme"});document.head.appendChild(o);let n=Wt("(prefers-color-scheme: light)");return H(()=>{let i=new T;return i.subscribe(s=>{if(document.body.setAttribute("data-md-color-switching",""),s.color.media==="(prefers-color-scheme)"){let a=matchMedia("(prefers-color-scheme: light)"),c=document.querySelector(a.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");s.color.scheme=c.getAttribute("data-md-color-scheme"),s.color.primary=c.getAttribute("data-md-color-primary"),s.color.accent=c.getAttribute("data-md-color-accent")}for(let[a,c]of Object.entries(s.color))document.body.setAttribute(`data-md-color-${a}`,c);for(let a=0;as.key==="Enter"),te(i,(s,a)=>a)).subscribe(({index:s})=>{s=(s+1)%t.length,t[s].click(),t[s].focus()}),i.pipe(m(()=>{let s=Ce("header"),a=window.getComputedStyle(s);return o.content=a.colorScheme,a.backgroundColor.match(/\d+/g).map(c=>(+c).toString(16).padStart(2,"0")).join("")})).subscribe(s=>r.content=`#${s}`),i.pipe(xe(pe)).subscribe(()=>{document.body.removeAttribute("data-md-color-switching")}),us(t).pipe(W(n.pipe(Ie(1))),vt(),O(s=>i.next(s)),A(()=>i.complete()),m(s=>P({ref:e},s)))})}function mi(e,{progress$:t}){return H(()=>{let r=new T;return r.subscribe(({value:o})=>{e.style.setProperty("--md-progress-value",`${o}`)}),t.pipe(O(o=>r.next({value:o})),A(()=>r.complete()),m(o=>({ref:e,value:o})))})}function fi(e,t){return e.protocol=t.protocol,e.hostname=t.hostname,e}function ds(e,t){let r=new Map;for(let o of M("url",e)){let n=j("loc",o),i=[fi(new URL(n.textContent),t)];r.set(`${i[0]}`,i);for(let s of M("[rel=alternate]",o)){let a=s.getAttribute("href");a!=null&&i.push(fi(new URL(a),t))}}return r}function kt(e){return En(new URL("sitemap.xml",e)).pipe(m(t=>ds(t,new URL(e))),ve(()=>$(new Map)),le())}function ui({document$:e}){let t=new Map;e.pipe(b(()=>M("link[rel=alternate]")),m(r=>new URL(r.href)),g(r=>!t.has(r.toString())),J(r=>kt(r).pipe(m(o=>[r,o]),ve(()=>y)))).subscribe(([r,o])=>{t.set(r.toString().replace(/\/$/,""),o)}),h(document.body,"click").pipe(g(r=>!r.metaKey&&!r.ctrlKey),b(r=>{if(r.target instanceof Element){let o=r.target.closest("a");if(o&&!o.target){let n=[...t].find(([f])=>o.href.startsWith(`${f}/`));if(typeof n=="undefined")return y;let[i,s]=n,a=we();if(a.href.startsWith(i))return y;let c=Te(),p=a.href.replace(c.base,"");p=`${i}/${p}`;let l=s.has(p.split("#")[0])?new URL(p,c.base):new URL(i);return r.preventDefault(),$(l)}}return y})).subscribe(r=>st(r,!0))}var co=$t(ao());function hs(e){e.setAttribute("data-md-copying","");let t=e.closest("[data-copy]"),r=t?t.getAttribute("data-copy"):e.innerText;return e.removeAttribute("data-md-copying"),r.trimEnd()}function di({alert$:e}){co.default.isSupported()&&new F(t=>{new co.default("[data-clipboard-target], [data-clipboard-text]",{text:r=>r.getAttribute("data-clipboard-text")||hs(j(r.getAttribute("data-clipboard-target")))}).on("success",r=>t.next(r))}).pipe(O(t=>{t.trigger.focus()}),m(()=>Me("clipboard.copied"))).subscribe(e)}function hi(e,t){if(!(e.target instanceof Element))return y;let r=e.target.closest("a");if(r===null)return y;if(r.target||e.metaKey||e.ctrlKey)return y;let o=new URL(r.href);return o.search=o.hash="",t.has(`${o}`)?(e.preventDefault(),$(r)):y}function bi(e){let t=new Map;for(let r of M(":scope > *",e.head))t.set(r.outerHTML,r);return t}function vi(e){for(let t of M("[href], [src]",e))for(let r of["href","src"]){let o=t.getAttribute(r);if(o&&!/^(?:[a-z]+:)?\/\//i.test(o)){t[r]=t[r];break}}return $(e)}function bs(e){for(let o of["[data-md-component=announce]","[data-md-component=container]","[data-md-component=header-topic]","[data-md-component=outdated]","[data-md-component=logo]","[data-md-component=skip]",...V("navigation.tabs.sticky")?["[data-md-component=tabs]"]:[]]){let n=ue(o),i=ue(o,e);typeof n!="undefined"&&typeof i!="undefined"&&n.replaceWith(i)}let t=bi(document);for(let[o,n]of bi(e))t.has(o)?t.delete(o):document.head.appendChild(n);for(let o of t.values()){let n=o.getAttribute("name");n!=="theme-color"&&n!=="color-scheme"&&o.remove()}let r=Ce("container");return Ke(M("script",r)).pipe(b(o=>{let n=e.createElement("script");if(o.src){for(let i of o.getAttributeNames())n.setAttribute(i,o.getAttribute(i));return o.replaceWith(n),new F(i=>{n.onload=()=>i.complete()})}else return n.textContent=o.textContent,o.replaceWith(n),y}),oe(),ae(document))}function gi({sitemap$:e,location$:t,viewport$:r,progress$:o}){if(location.protocol==="file:")return y;$(document).subscribe(vi);let n=h(document.body,"click").pipe(Pe(e),b(([a,c])=>hi(a,c)),m(({href:a})=>new URL(a)),le()),i=h(window,"popstate").pipe(m(we),le());n.pipe(te(r)).subscribe(([a,{offset:c}])=>{history.replaceState(c,""),history.pushState(null,"",a)}),L(n,i).subscribe(t);let s=t.pipe(ne("pathname"),b(a=>xr(a,{progress$:o}).pipe(ve(()=>(st(a,!0),y)))),b(vi),b(bs),le());return L(s.pipe(te(t,(a,c)=>c)),s.pipe(b(()=>t),ne("hash")),t.pipe(Y((a,c)=>a.pathname===c.pathname&&a.hash===c.hash),b(()=>n),O(()=>history.back()))).subscribe(a=>{var c,p;history.state!==null||!a.hash?window.scrollTo(0,(p=(c=history.state)==null?void 0:c.y)!=null?p:0):(history.scrollRestoration="auto",gn(a.hash),history.scrollRestoration="manual")}),t.subscribe(()=>{history.scrollRestoration="manual"}),h(window,"beforeunload").subscribe(()=>{history.scrollRestoration="auto"}),r.pipe(ne("offset"),Ae(100)).subscribe(({offset:a})=>{history.replaceState(a,"")}),V("navigation.instant.prefetch")&&L(h(document.body,"mousemove"),h(document.body,"focusin")).pipe(Pe(e),b(([a,c])=>hi(a,c)),Ae(25),Qr(({href:a})=>a),hr(a=>{let c=document.createElement("link");return c.rel="prefetch",c.href=a.toString(),document.head.appendChild(c),h(c,"load").pipe(m(()=>c),Ee(1))})).subscribe(a=>a.remove()),s}var yi=$t(ro());function xi(e){let t=e.separator.split("|").map(n=>n.replace(/(\(\?[!=<][^)]+\))/g,"").length===0?"\uFFFD":n).join("|"),r=new RegExp(t,"img"),o=(n,i,s)=>`${i}${s}`;return n=>{n=n.replace(/[\s*+\-:~^]+/g," ").replace(/&/g,"&").trim();let i=new RegExp(`(^|${e.separator}|)(${n.replace(/[|\\{}()[\]^$+*?.-]/g,"\\$&").replace(r,"|")})`,"img");return s=>(0,yi.default)(s).replace(i,o).replace(/<\/mark>(\s+)]*>/img,"$1")}}function zt(e){return e.type===1}function Sr(e){return e.type===3}function Ei(e,t){let r=Mn(e);return L($(location.protocol!=="file:"),Je("search")).pipe(Re(o=>o),b(()=>t)).subscribe(({config:o,docs:n})=>r.next({type:0,data:{config:o,docs:n,options:{suggest:V("search.suggest")}}})),r}function wi(e){var l;let{selectedVersionSitemap:t,selectedVersionBaseURL:r,currentLocation:o,currentBaseURL:n}=e,i=(l=po(n))==null?void 0:l.pathname;if(i===void 0)return;let s=ys(o.pathname,i);if(s===void 0)return;let a=Es(t.keys());if(!t.has(a))return;let c=po(s,a);if(!c||!t.has(c.href))return;let p=po(s,r);if(p)return p.hash=o.hash,p.search=o.search,p}function po(e,t){try{return new URL(e,t)}catch(r){return}}function ys(e,t){if(e.startsWith(t))return e.slice(t.length)}function xs(e,t){let r=Math.min(e.length,t.length),o;for(o=0;oy)),o=r.pipe(m(n=>{let[,i]=t.base.match(/([^/]+)\/?$/);return n.find(({version:s,aliases:a})=>s===i||a.includes(i))||n[0]}));r.pipe(m(n=>new Map(n.map(i=>[`${new URL(`../${i.version}/`,t.base)}`,i]))),b(n=>h(document.body,"click").pipe(g(i=>!i.metaKey&&!i.ctrlKey),te(o),b(([i,s])=>{if(i.target instanceof Element){let a=i.target.closest("a");if(a&&!a.target&&n.has(a.href)){let c=a.href;return!i.target.closest(".md-version")&&n.get(c)===s?y:(i.preventDefault(),$(new URL(c)))}}return y}),b(i=>kt(i).pipe(m(s=>{var a;return(a=wi({selectedVersionSitemap:s,selectedVersionBaseURL:i,currentLocation:we(),currentBaseURL:t.base}))!=null?a:i})))))).subscribe(n=>st(n,!0)),z([r,o]).subscribe(([n,i])=>{j(".md-header__topic").appendChild(Wn(n,i))}),e.pipe(b(()=>o)).subscribe(n=>{var a;let i=new URL(t.base),s=__md_get("__outdated",sessionStorage,i);if(s===null){s=!0;let c=((a=t.version)==null?void 0:a.default)||"latest";Array.isArray(c)||(c=[c]);e:for(let p of c)for(let l of n.aliases.concat(n.version))if(new RegExp(p,"i").test(l)){s=!1;break e}__md_set("__outdated",s,sessionStorage,i)}if(s)for(let c of me("outdated"))c.hidden=!1})}function ws(e,{worker$:t}){let{searchParams:r}=we();r.has("q")&&(at("search",!0),e.value=r.get("q"),e.focus(),Je("search").pipe(Re(i=>!i)).subscribe(()=>{let i=we();i.searchParams.delete("q"),history.replaceState({},"",`${i}`)}));let o=Ye(e),n=L(t.pipe(Re(zt)),h(e,"keyup"),o).pipe(m(()=>e.value),Y());return z([n,o]).pipe(m(([i,s])=>({value:i,focus:s})),Z(1))}function Si(e,{worker$:t}){let r=new T,o=r.pipe(oe(),ae(!0));z([t.pipe(Re(zt)),r],(i,s)=>s).pipe(ne("value")).subscribe(({value:i})=>t.next({type:2,data:i})),r.pipe(ne("focus")).subscribe(({focus:i})=>{i&&at("search",i)}),h(e.form,"reset").pipe(W(o)).subscribe(()=>e.focus());let n=j("header [for=__search]");return h(n,"click").subscribe(()=>e.focus()),ws(e,{worker$:t}).pipe(O(i=>r.next(i)),A(()=>r.complete()),m(i=>P({ref:e},i)),Z(1))}function Oi(e,{worker$:t,query$:r}){let o=new T,n=un(e.parentElement).pipe(g(Boolean)),i=e.parentElement,s=j(":scope > :first-child",e),a=j(":scope > :last-child",e);Je("search").subscribe(l=>{a.setAttribute("role",l?"list":"presentation"),a.hidden=!l}),o.pipe(te(r),Gr(t.pipe(Re(zt)))).subscribe(([{items:l},{value:f}])=>{switch(l.length){case 0:s.textContent=f.length?Me("search.result.none"):Me("search.result.placeholder");break;case 1:s.textContent=Me("search.result.one");break;default:let u=br(l.length);s.textContent=Me("search.result.other",u)}});let c=o.pipe(O(()=>a.innerHTML=""),b(({items:l})=>L($(...l.slice(0,10)),$(...l.slice(10)).pipe(ot(4),Xr(n),b(([f])=>f)))),m(Fn),le());return c.subscribe(l=>a.appendChild(l)),c.pipe(J(l=>{let f=ue("details",l);return typeof f=="undefined"?y:h(f,"toggle").pipe(W(o),m(()=>f))})).subscribe(l=>{l.open===!1&&l.offsetTop<=i.scrollTop&&i.scrollTo({top:l.offsetTop})}),t.pipe(g(Sr),m(({data:l})=>l)).pipe(O(l=>o.next(l)),A(()=>o.complete()),m(l=>P({ref:e},l)))}function Ts(e,{query$:t}){return t.pipe(m(({value:r})=>{let o=we();return o.hash="",r=r.replace(/\s+/g,"+").replace(/&/g,"%26").replace(/=/g,"%3D"),o.search=`q=${r}`,{url:o}}))}function Li(e,t){let r=new T,o=r.pipe(oe(),ae(!0));return r.subscribe(({url:n})=>{e.setAttribute("data-clipboard-text",e.href),e.href=`${n}`}),h(e,"click").pipe(W(o)).subscribe(n=>n.preventDefault()),Ts(e,t).pipe(O(n=>r.next(n)),A(()=>r.complete()),m(n=>P({ref:e},n)))}function Mi(e,{worker$:t,keyboard$:r}){let o=new T,n=Ce("search-query"),i=L(h(n,"keydown"),h(n,"focus")).pipe(xe(pe),m(()=>n.value),Y());return o.pipe(Pe(i),m(([{suggest:a},c])=>{let p=c.split(/([\s-]+)/);if(a!=null&&a.length&&p[p.length-1]){let l=a[a.length-1];l.startsWith(p[p.length-1])&&(p[p.length-1]=l)}else p.length=0;return p})).subscribe(a=>e.innerHTML=a.join("").replace(/\s/g," ")),r.pipe(g(({mode:a})=>a==="search")).subscribe(a=>{a.type==="ArrowRight"&&e.innerText.length&&n.selectionStart===n.value.length&&(n.value=e.innerText)}),t.pipe(g(Sr),m(({data:a})=>a)).pipe(O(a=>o.next(a)),A(()=>o.complete()),m(()=>({ref:e})))}function _i(e,{index$:t,keyboard$:r}){let o=Te();try{let n=Ei(o.search,t),i=Ce("search-query",e),s=Ce("search-result",e);h(e,"click").pipe(g(({target:c})=>c instanceof Element&&!!c.closest("a"))).subscribe(()=>at("search",!1)),r.pipe(g(({mode:c})=>c==="search")).subscribe(c=>{let p=Ne();switch(c.type){case"Enter":if(p===i){let l=new Map;for(let f of M(":first-child [href]",s)){let u=f.firstElementChild;l.set(f,parseFloat(u.getAttribute("data-md-score")))}if(l.size){let[[f]]=[...l].sort(([,u],[,d])=>d-u);f.click()}c.claim()}break;case"Escape":case"Tab":at("search",!1),i.blur();break;case"ArrowUp":case"ArrowDown":if(typeof p=="undefined")i.focus();else{let l=[i,...M(":not(details) > [href], summary, details[open] [href]",s)],f=Math.max(0,(Math.max(0,l.indexOf(p))+l.length+(c.type==="ArrowUp"?-1:1))%l.length);l[f].focus()}c.claim();break;default:i!==Ne()&&i.focus()}}),r.pipe(g(({mode:c})=>c==="global")).subscribe(c=>{switch(c.type){case"f":case"s":case"/":i.focus(),i.select(),c.claim();break}});let a=Si(i,{worker$:n});return L(a,Oi(s,{worker$:n,query$:a})).pipe(Ve(...me("search-share",e).map(c=>Li(c,{query$:a})),...me("search-suggest",e).map(c=>Mi(c,{worker$:n,keyboard$:r}))))}catch(n){return e.hidden=!0,tt}}function Ai(e,{index$:t,location$:r}){return z([t,r.pipe(Q(we()),g(o=>!!o.searchParams.get("h")))]).pipe(m(([o,n])=>xi(o.config)(n.searchParams.get("h"))),m(o=>{var s;let n=new Map,i=document.createNodeIterator(e,NodeFilter.SHOW_TEXT);for(let a=i.nextNode();a;a=i.nextNode())if((s=a.parentElement)!=null&&s.offsetHeight){let c=a.textContent,p=o(c);p.length>c.length&&n.set(a,p)}for(let[a,c]of n){let{childNodes:p}=x("span",null,c);a.replaceWith(...Array.from(p))}return{ref:e,nodes:n}}))}function Ss(e,{viewport$:t,main$:r}){let o=e.closest(".md-grid"),n=o.offsetTop-o.parentElement.offsetTop;return z([r,t]).pipe(m(([{offset:i,height:s},{offset:{y:a}}])=>(s=s+Math.min(n,Math.max(0,a-i))-n,{height:s,locked:a>=i+n})),Y((i,s)=>i.height===s.height&&i.locked===s.locked))}function lo(e,o){var n=o,{header$:t}=n,r=vo(n,["header$"]);let i=j(".md-sidebar__scrollwrap",e),{y:s}=Be(i);return H(()=>{let a=new T,c=a.pipe(oe(),ae(!0)),p=a.pipe($e(0,ye));return p.pipe(te(t)).subscribe({next([{height:l},{height:f}]){i.style.height=`${l-2*s}px`,e.style.top=`${f}px`},complete(){i.style.height="",e.style.top=""}}),p.pipe(Re()).subscribe(()=>{for(let l of M(".md-nav__link--active[href]",e)){if(!l.clientHeight)continue;let f=l.closest(".md-sidebar__scrollwrap");if(typeof f!="undefined"){let u=l.offsetTop-f.offsetTop,{height:d}=de(f);f.scrollTo({top:u-d/2})}}}),fe(M("label[tabindex]",e)).pipe(J(l=>h(l,"click").pipe(xe(pe),m(()=>l),W(c)))).subscribe(l=>{let f=j(`[id="${l.htmlFor}"]`);j(`[aria-labelledby="${l.id}"]`).setAttribute("aria-expanded",`${f.checked}`)}),V("content.tooltips")&&fe(M("abbr[title]",e)).pipe(J(l=>Xe(l,{viewport$})),W(c)).subscribe(),Ss(e,r).pipe(O(l=>a.next(l)),A(()=>a.complete()),m(l=>P({ref:e},l)))})}function Ci(e,t){if(typeof t!="undefined"){let r=`https://api.github.com/repos/${e}/${t}`;return rt(ze(`${r}/releases/latest`).pipe(ve(()=>y),m(o=>({version:o.tag_name})),Qe({})),ze(r).pipe(ve(()=>y),m(o=>({stars:o.stargazers_count,forks:o.forks_count})),Qe({}))).pipe(m(([o,n])=>P(P({},o),n)))}else{let r=`https://api.github.com/users/${e}`;return ze(r).pipe(m(o=>({repositories:o.public_repos})),Qe({}))}}function ki(e,t){let r=`https://${e}/api/v4/projects/${encodeURIComponent(t)}`;return rt(ze(`${r}/releases/permalink/latest`).pipe(ve(()=>y),m(({tag_name:o})=>({version:o})),Qe({})),ze(r).pipe(ve(()=>y),m(({star_count:o,forks_count:n})=>({stars:o,forks:n})),Qe({}))).pipe(m(([o,n])=>P(P({},o),n)))}function Hi(e){let t=e.match(/^.+github\.com\/([^/]+)\/?([^/]+)?/i);if(t){let[,r,o]=t;return Ci(r,o)}if(t=e.match(/^.+?([^/]*gitlab[^/]+)\/(.+?)\/?$/i),t){let[,r,o]=t;return ki(r,o)}return y}var Os;function Ls(e){return Os||(Os=H(()=>{let t=__md_get("__source",sessionStorage);if(t)return $(t);if(me("consent").length){let o=__md_get("__consent");if(!(o&&o.github))return y}return Hi(e.href).pipe(O(o=>__md_set("__source",o,sessionStorage)))}).pipe(ve(()=>y),g(t=>Object.keys(t).length>0),m(t=>({facts:t})),Z(1)))}function $i(e){let t=j(":scope > :last-child",e);return H(()=>{let r=new T;return r.subscribe(({facts:o})=>{t.appendChild(jn(o)),t.classList.add("md-source__repository--active")}),Ls(e).pipe(O(o=>r.next(o)),A(()=>r.complete()),m(o=>P({ref:e},o)))})}function Ms(e,{viewport$:t,header$:r}){return Le(document.body).pipe(b(()=>Er(e,{header$:r,viewport$:t})),m(({offset:{y:o}})=>({hidden:o>=10})),ne("hidden"))}function Pi(e,t){return H(()=>{let r=new T;return r.subscribe({next({hidden:o}){e.hidden=o},complete(){e.hidden=!1}}),(V("navigation.tabs.sticky")?$({hidden:!1}):Ms(e,t)).pipe(O(o=>r.next(o)),A(()=>r.complete()),m(o=>P({ref:e},o)))})}function _s(e,{viewport$:t,header$:r}){let o=new Map,n=M(".md-nav__link",e);for(let a of n){let c=decodeURIComponent(a.hash.substring(1)),p=ue(`[id="${c}"]`);typeof p!="undefined"&&o.set(a,p)}let i=r.pipe(ne("height"),m(({height:a})=>{let c=Ce("main"),p=j(":scope > :first-child",c);return a+.8*(p.offsetTop-c.offsetTop)}),le());return Le(document.body).pipe(ne("height"),b(a=>H(()=>{let c=[];return $([...o].reduce((p,[l,f])=>{for(;c.length&&o.get(c[c.length-1]).tagName>=f.tagName;)c.pop();let u=f.offsetTop;for(;!u&&f.parentElement;)f=f.parentElement,u=f.offsetTop;let d=f.offsetParent;for(;d;d=d.offsetParent)u+=d.offsetTop;return p.set([...c=[...c,l]].reverse(),u)},new Map))}).pipe(m(c=>new Map([...c].sort(([,p],[,l])=>p-l))),Pe(i),b(([c,p])=>t.pipe(Ut(([l,f],{offset:{y:u},size:d})=>{let v=u+d.height>=Math.floor(a.height);for(;f.length;){let[,S]=f[0];if(S-p=u&&!v)f=[l.pop(),...f];else break}return[l,f]},[[],[...c]]),Y((l,f)=>l[0]===f[0]&&l[1]===f[1])))))).pipe(m(([a,c])=>({prev:a.map(([p])=>p),next:c.map(([p])=>p)})),Q({prev:[],next:[]}),ot(2,1),m(([a,c])=>a.prev.length{let i=new T,s=i.pipe(oe(),ae(!0));if(i.subscribe(({prev:a,next:c})=>{for(let[p]of c)p.classList.remove("md-nav__link--passed"),p.classList.remove("md-nav__link--active");for(let[p,[l]]of a.entries())l.classList.add("md-nav__link--passed"),l.classList.toggle("md-nav__link--active",p===a.length-1)}),V("toc.follow")){let a=L(t.pipe(Ae(1),m(()=>{})),t.pipe(Ae(250),m(()=>"smooth")));i.pipe(g(({prev:c})=>c.length>0),Pe(o.pipe(xe(pe))),te(a)).subscribe(([[{prev:c}],p])=>{let[l]=c[c.length-1];if(l.offsetHeight){let f=vr(l);if(typeof f!="undefined"){let u=l.offsetTop-f.offsetTop,{height:d}=de(f);f.scrollTo({top:u-d/2,behavior:p})}}})}return V("navigation.tracking")&&t.pipe(W(s),ne("offset"),Ae(250),Ie(1),W(n.pipe(Ie(1))),vt({delay:250}),te(i)).subscribe(([,{prev:a}])=>{let c=we(),p=a[a.length-1];if(p&&p.length){let[l]=p,{hash:f}=new URL(l.href);c.hash!==f&&(c.hash=f,history.replaceState({},"",`${c}`))}else c.hash="",history.replaceState({},"",`${c}`)}),_s(e,{viewport$:t,header$:r}).pipe(O(a=>i.next(a)),A(()=>i.complete()),m(a=>P({ref:e},a)))})}function As(e,{viewport$:t,main$:r,target$:o}){let n=t.pipe(m(({offset:{y:s}})=>s),ot(2,1),m(([s,a])=>s>a&&a>0),Y()),i=r.pipe(m(({active:s})=>s));return z([i,n]).pipe(m(([s,a])=>!(s&&a)),Y(),W(o.pipe(Ie(1))),ae(!0),vt({delay:250}),m(s=>({hidden:s})))}function Ii(e,{viewport$:t,header$:r,main$:o,target$:n}){let i=new T,s=i.pipe(oe(),ae(!0));return i.subscribe({next({hidden:a}){e.hidden=a,a?(e.setAttribute("tabindex","-1"),e.blur()):e.removeAttribute("tabindex")},complete(){e.style.top="",e.hidden=!0,e.removeAttribute("tabindex")}}),r.pipe(W(s),ne("height")).subscribe(({height:a})=>{e.style.top=`${a+16}px`}),h(e,"click").subscribe(a=>{a.preventDefault(),window.scrollTo({top:0})}),As(e,{viewport$:t,main$:o,target$:n}).pipe(O(a=>i.next(a)),A(()=>i.complete()),m(a=>P({ref:e},a)))}function Fi({document$:e,viewport$:t}){e.pipe(b(()=>M(".md-ellipsis")),J(r=>mt(r).pipe(W(e.pipe(Ie(1))),g(o=>o),m(()=>r),Ee(1))),g(r=>r.offsetWidth{let o=r.innerText,n=r.closest("a")||r;return n.title=o,V("content.tooltips")?Xe(n,{viewport$:t}).pipe(W(e.pipe(Ie(1))),A(()=>n.removeAttribute("title"))):y})).subscribe(),V("content.tooltips")&&e.pipe(b(()=>M(".md-status")),J(r=>Xe(r,{viewport$:t}))).subscribe()}function ji({document$:e,tablet$:t}){e.pipe(b(()=>M(".md-toggle--indeterminate")),O(r=>{r.indeterminate=!0,r.checked=!1}),J(r=>h(r,"change").pipe(Jr(()=>r.classList.contains("md-toggle--indeterminate")),m(()=>r))),te(t)).subscribe(([r,o])=>{r.classList.remove("md-toggle--indeterminate"),o&&(r.checked=!1)})}function Cs(){return/(iPad|iPhone|iPod)/.test(navigator.userAgent)}function Ui({document$:e}){e.pipe(b(()=>M("[data-md-scrollfix]")),O(t=>t.removeAttribute("data-md-scrollfix")),g(Cs),J(t=>h(t,"touchstart").pipe(m(()=>t)))).subscribe(t=>{let r=t.scrollTop;r===0?t.scrollTop=1:r+t.offsetHeight===t.scrollHeight&&(t.scrollTop=r-1)})}function Wi({viewport$:e,tablet$:t}){z([Je("search"),t]).pipe(m(([r,o])=>r&&!o),b(r=>$(r).pipe(nt(r?400:100))),te(e)).subscribe(([r,{offset:{y:o}}])=>{if(r)document.body.setAttribute("data-md-scrolllock",""),document.body.style.top=`-${o}px`;else{let n=-1*parseInt(document.body.style.top,10);document.body.removeAttribute("data-md-scrolllock"),document.body.style.top="",n&&window.scrollTo(0,n)}})}Object.entries||(Object.entries=function(e){let t=[];for(let r of Object.keys(e))t.push([r,e[r]]);return t});Object.values||(Object.values=function(e){let t=[];for(let r of Object.keys(e))t.push(e[r]);return t});typeof Element!="undefined"&&(Element.prototype.scrollTo||(Element.prototype.scrollTo=function(e,t){typeof e=="object"?(this.scrollLeft=e.left,this.scrollTop=e.top):(this.scrollLeft=e,this.scrollTop=t)}),Element.prototype.replaceWith||(Element.prototype.replaceWith=function(...e){let t=this.parentNode;if(t){e.length===0&&t.removeChild(this);for(let r=e.length-1;r>=0;r--){let o=e[r];typeof o=="string"?o=document.createTextNode(o):o.parentNode&&o.parentNode.removeChild(o),r?t.insertBefore(this.previousSibling,o):t.replaceChild(o,this)}}}));function ks(){return location.protocol==="file:"?_t(`${new URL("search/search_index.js",Or.base)}`).pipe(m(()=>__index),Z(1)):ze(new URL("search/search_index.json",Or.base))}document.documentElement.classList.remove("no-js");document.documentElement.classList.add("js");var ct=an(),Kt=bn(),Ht=yn(Kt),mo=hn(),ke=Ln(),Lr=Wt("(min-width: 60em)"),Vi=Wt("(min-width: 76.25em)"),Ni=xn(),Or=Te(),zi=document.forms.namedItem("search")?ks():tt,fo=new T;di({alert$:fo});ui({document$:ct});var uo=new T,qi=kt(Or.base);V("navigation.instant")&&gi({sitemap$:qi,location$:Kt,viewport$:ke,progress$:uo}).subscribe(ct);var Di;((Di=Or.version)==null?void 0:Di.provider)==="mike"&&Ti({document$:ct});L(Kt,Ht).pipe(nt(125)).subscribe(()=>{at("drawer",!1),at("search",!1)});mo.pipe(g(({mode:e})=>e==="global")).subscribe(e=>{switch(e.type){case"p":case",":let t=ue("link[rel=prev]");typeof t!="undefined"&&st(t);break;case"n":case".":let r=ue("link[rel=next]");typeof r!="undefined"&&st(r);break;case"Enter":let o=Ne();o instanceof HTMLLabelElement&&o.click()}});Fi({viewport$:ke,document$:ct});ji({document$:ct,tablet$:Lr});Ui({document$:ct});Wi({viewport$:ke,tablet$:Lr});var ft=ai(Ce("header"),{viewport$:ke}),qt=ct.pipe(m(()=>Ce("main")),b(e=>pi(e,{viewport$:ke,header$:ft})),Z(1)),Hs=L(...me("consent").map(e=>An(e,{target$:Ht})),...me("dialog").map(e=>ni(e,{alert$:fo})),...me("palette").map(e=>li(e)),...me("progress").map(e=>mi(e,{progress$:uo})),...me("search").map(e=>_i(e,{index$:zi,keyboard$:mo})),...me("source").map(e=>$i(e))),$s=H(()=>L(...me("announce").map(e=>_n(e)),...me("content").map(e=>oi(e,{sitemap$:qi,viewport$:ke,target$:Ht,print$:Ni})),...me("content").map(e=>V("search.highlight")?Ai(e,{index$:zi,location$:Kt}):y),...me("header").map(e=>si(e,{viewport$:ke,header$:ft,main$:qt})),...me("header-title").map(e=>ci(e,{viewport$:ke,header$:ft})),...me("sidebar").map(e=>e.getAttribute("data-md-type")==="navigation"?eo(Vi,()=>lo(e,{viewport$:ke,header$:ft,main$:qt})):eo(Lr,()=>lo(e,{viewport$:ke,header$:ft,main$:qt}))),...me("tabs").map(e=>Pi(e,{viewport$:ke,header$:ft})),...me("toc").map(e=>Ri(e,{viewport$:ke,header$:ft,main$:qt,target$:Ht})),...me("top").map(e=>Ii(e,{viewport$:ke,header$:ft,main$:qt,target$:Ht})))),Ki=ct.pipe(b(()=>$s),Ve(Hs),Z(1));Ki.subscribe();window.document$=ct;window.location$=Kt;window.target$=Ht;window.keyboard$=mo;window.viewport$=ke;window.tablet$=Lr;window.screen$=Vi;window.print$=Ni;window.alert$=fo;window.progress$=uo;window.component$=Ki;})(); +//# sourceMappingURL=bundle.79ae519e.min.js.map + diff --git a/en/assets/javascripts/bundle.79ae519e.min.js.map b/en/assets/javascripts/bundle.79ae519e.min.js.map new file mode 100644 index 0000000..5cf0289 --- /dev/null +++ b/en/assets/javascripts/bundle.79ae519e.min.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["node_modules/focus-visible/dist/focus-visible.js", "node_modules/escape-html/index.js", "node_modules/clipboard/dist/clipboard.js", "src/templates/assets/javascripts/bundle.ts", "node_modules/tslib/tslib.es6.mjs", "node_modules/rxjs/src/internal/util/isFunction.ts", "node_modules/rxjs/src/internal/util/createErrorClass.ts", "node_modules/rxjs/src/internal/util/UnsubscriptionError.ts", "node_modules/rxjs/src/internal/util/arrRemove.ts", "node_modules/rxjs/src/internal/Subscription.ts", "node_modules/rxjs/src/internal/config.ts", "node_modules/rxjs/src/internal/scheduler/timeoutProvider.ts", "node_modules/rxjs/src/internal/util/reportUnhandledError.ts", "node_modules/rxjs/src/internal/util/noop.ts", "node_modules/rxjs/src/internal/NotificationFactories.ts", "node_modules/rxjs/src/internal/util/errorContext.ts", "node_modules/rxjs/src/internal/Subscriber.ts", "node_modules/rxjs/src/internal/symbol/observable.ts", "node_modules/rxjs/src/internal/util/identity.ts", "node_modules/rxjs/src/internal/util/pipe.ts", "node_modules/rxjs/src/internal/Observable.ts", "node_modules/rxjs/src/internal/util/lift.ts", "node_modules/rxjs/src/internal/operators/OperatorSubscriber.ts", "node_modules/rxjs/src/internal/scheduler/animationFrameProvider.ts", "node_modules/rxjs/src/internal/util/ObjectUnsubscribedError.ts", "node_modules/rxjs/src/internal/Subject.ts", "node_modules/rxjs/src/internal/BehaviorSubject.ts", "node_modules/rxjs/src/internal/scheduler/dateTimestampProvider.ts", "node_modules/rxjs/src/internal/ReplaySubject.ts", "node_modules/rxjs/src/internal/scheduler/Action.ts", "node_modules/rxjs/src/internal/scheduler/intervalProvider.ts", "node_modules/rxjs/src/internal/scheduler/AsyncAction.ts", "node_modules/rxjs/src/internal/Scheduler.ts", "node_modules/rxjs/src/internal/scheduler/AsyncScheduler.ts", "node_modules/rxjs/src/internal/scheduler/async.ts", "node_modules/rxjs/src/internal/scheduler/QueueAction.ts", "node_modules/rxjs/src/internal/scheduler/QueueScheduler.ts", "node_modules/rxjs/src/internal/scheduler/queue.ts", "node_modules/rxjs/src/internal/scheduler/AnimationFrameAction.ts", "node_modules/rxjs/src/internal/scheduler/AnimationFrameScheduler.ts", "node_modules/rxjs/src/internal/scheduler/animationFrame.ts", "node_modules/rxjs/src/internal/observable/empty.ts", "node_modules/rxjs/src/internal/util/isScheduler.ts", "node_modules/rxjs/src/internal/util/args.ts", "node_modules/rxjs/src/internal/util/isArrayLike.ts", "node_modules/rxjs/src/internal/util/isPromise.ts", "node_modules/rxjs/src/internal/util/isInteropObservable.ts", "node_modules/rxjs/src/internal/util/isAsyncIterable.ts", "node_modules/rxjs/src/internal/util/throwUnobservableError.ts", "node_modules/rxjs/src/internal/symbol/iterator.ts", "node_modules/rxjs/src/internal/util/isIterable.ts", "node_modules/rxjs/src/internal/util/isReadableStreamLike.ts", "node_modules/rxjs/src/internal/observable/innerFrom.ts", "node_modules/rxjs/src/internal/util/executeSchedule.ts", "node_modules/rxjs/src/internal/operators/observeOn.ts", "node_modules/rxjs/src/internal/operators/subscribeOn.ts", "node_modules/rxjs/src/internal/scheduled/scheduleObservable.ts", "node_modules/rxjs/src/internal/scheduled/schedulePromise.ts", "node_modules/rxjs/src/internal/scheduled/scheduleArray.ts", "node_modules/rxjs/src/internal/scheduled/scheduleIterable.ts", "node_modules/rxjs/src/internal/scheduled/scheduleAsyncIterable.ts", "node_modules/rxjs/src/internal/scheduled/scheduleReadableStreamLike.ts", "node_modules/rxjs/src/internal/scheduled/scheduled.ts", "node_modules/rxjs/src/internal/observable/from.ts", "node_modules/rxjs/src/internal/observable/of.ts", "node_modules/rxjs/src/internal/observable/throwError.ts", "node_modules/rxjs/src/internal/util/EmptyError.ts", "node_modules/rxjs/src/internal/util/isDate.ts", "node_modules/rxjs/src/internal/operators/map.ts", "node_modules/rxjs/src/internal/util/mapOneOrManyArgs.ts", "node_modules/rxjs/src/internal/util/argsArgArrayOrObject.ts", "node_modules/rxjs/src/internal/util/createObject.ts", "node_modules/rxjs/src/internal/observable/combineLatest.ts", "node_modules/rxjs/src/internal/operators/mergeInternals.ts", "node_modules/rxjs/src/internal/operators/mergeMap.ts", "node_modules/rxjs/src/internal/operators/mergeAll.ts", "node_modules/rxjs/src/internal/operators/concatAll.ts", "node_modules/rxjs/src/internal/observable/concat.ts", "node_modules/rxjs/src/internal/observable/defer.ts", "node_modules/rxjs/src/internal/observable/fromEvent.ts", "node_modules/rxjs/src/internal/observable/fromEventPattern.ts", "node_modules/rxjs/src/internal/observable/timer.ts", "node_modules/rxjs/src/internal/observable/merge.ts", "node_modules/rxjs/src/internal/observable/never.ts", "node_modules/rxjs/src/internal/util/argsOrArgArray.ts", "node_modules/rxjs/src/internal/operators/filter.ts", "node_modules/rxjs/src/internal/observable/zip.ts", "node_modules/rxjs/src/internal/operators/audit.ts", "node_modules/rxjs/src/internal/operators/auditTime.ts", "node_modules/rxjs/src/internal/operators/bufferCount.ts", "node_modules/rxjs/src/internal/operators/catchError.ts", "node_modules/rxjs/src/internal/operators/scanInternals.ts", "node_modules/rxjs/src/internal/operators/combineLatest.ts", "node_modules/rxjs/src/internal/operators/combineLatestWith.ts", "node_modules/rxjs/src/internal/operators/debounce.ts", "node_modules/rxjs/src/internal/operators/debounceTime.ts", "node_modules/rxjs/src/internal/operators/defaultIfEmpty.ts", "node_modules/rxjs/src/internal/operators/take.ts", "node_modules/rxjs/src/internal/operators/ignoreElements.ts", "node_modules/rxjs/src/internal/operators/mapTo.ts", "node_modules/rxjs/src/internal/operators/delayWhen.ts", "node_modules/rxjs/src/internal/operators/delay.ts", "node_modules/rxjs/src/internal/operators/distinct.ts", "node_modules/rxjs/src/internal/operators/distinctUntilChanged.ts", "node_modules/rxjs/src/internal/operators/distinctUntilKeyChanged.ts", "node_modules/rxjs/src/internal/operators/throwIfEmpty.ts", "node_modules/rxjs/src/internal/operators/endWith.ts", "node_modules/rxjs/src/internal/operators/exhaustMap.ts", "node_modules/rxjs/src/internal/operators/finalize.ts", "node_modules/rxjs/src/internal/operators/first.ts", "node_modules/rxjs/src/internal/operators/takeLast.ts", "node_modules/rxjs/src/internal/operators/merge.ts", "node_modules/rxjs/src/internal/operators/mergeWith.ts", "node_modules/rxjs/src/internal/operators/repeat.ts", "node_modules/rxjs/src/internal/operators/scan.ts", "node_modules/rxjs/src/internal/operators/share.ts", "node_modules/rxjs/src/internal/operators/shareReplay.ts", "node_modules/rxjs/src/internal/operators/skip.ts", "node_modules/rxjs/src/internal/operators/skipUntil.ts", "node_modules/rxjs/src/internal/operators/startWith.ts", "node_modules/rxjs/src/internal/operators/switchMap.ts", "node_modules/rxjs/src/internal/operators/takeUntil.ts", "node_modules/rxjs/src/internal/operators/takeWhile.ts", "node_modules/rxjs/src/internal/operators/tap.ts", "node_modules/rxjs/src/internal/operators/throttle.ts", "node_modules/rxjs/src/internal/operators/throttleTime.ts", "node_modules/rxjs/src/internal/operators/withLatestFrom.ts", "node_modules/rxjs/src/internal/operators/zip.ts", "node_modules/rxjs/src/internal/operators/zipWith.ts", "src/templates/assets/javascripts/browser/document/index.ts", "src/templates/assets/javascripts/browser/element/_/index.ts", "src/templates/assets/javascripts/browser/element/focus/index.ts", "src/templates/assets/javascripts/browser/element/hover/index.ts", "src/templates/assets/javascripts/utilities/h/index.ts", "src/templates/assets/javascripts/utilities/round/index.ts", "src/templates/assets/javascripts/browser/script/index.ts", "src/templates/assets/javascripts/browser/element/size/_/index.ts", "src/templates/assets/javascripts/browser/element/size/content/index.ts", "src/templates/assets/javascripts/browser/element/offset/_/index.ts", "src/templates/assets/javascripts/browser/element/offset/content/index.ts", "src/templates/assets/javascripts/browser/element/visibility/index.ts", "src/templates/assets/javascripts/browser/toggle/index.ts", "src/templates/assets/javascripts/browser/keyboard/index.ts", "src/templates/assets/javascripts/browser/location/_/index.ts", "src/templates/assets/javascripts/browser/location/hash/index.ts", "src/templates/assets/javascripts/browser/media/index.ts", "src/templates/assets/javascripts/browser/request/index.ts", "src/templates/assets/javascripts/browser/viewport/offset/index.ts", "src/templates/assets/javascripts/browser/viewport/size/index.ts", "src/templates/assets/javascripts/browser/viewport/_/index.ts", "src/templates/assets/javascripts/browser/viewport/at/index.ts", "src/templates/assets/javascripts/browser/worker/index.ts", "src/templates/assets/javascripts/_/index.ts", "src/templates/assets/javascripts/components/_/index.ts", "src/templates/assets/javascripts/components/announce/index.ts", "src/templates/assets/javascripts/components/consent/index.ts", "src/templates/assets/javascripts/templates/tooltip/index.tsx", "src/templates/assets/javascripts/templates/annotation/index.tsx", "src/templates/assets/javascripts/templates/clipboard/index.tsx", "src/templates/assets/javascripts/templates/search/index.tsx", "src/templates/assets/javascripts/templates/source/index.tsx", "src/templates/assets/javascripts/templates/tabbed/index.tsx", "src/templates/assets/javascripts/templates/table/index.tsx", "src/templates/assets/javascripts/templates/version/index.tsx", "src/templates/assets/javascripts/components/tooltip2/index.ts", "src/templates/assets/javascripts/components/content/annotation/_/index.ts", "src/templates/assets/javascripts/components/content/annotation/list/index.ts", "src/templates/assets/javascripts/components/content/annotation/block/index.ts", "src/templates/assets/javascripts/components/content/code/_/index.ts", "src/templates/assets/javascripts/components/content/details/index.ts", "src/templates/assets/javascripts/components/content/link/index.ts", "src/templates/assets/javascripts/components/content/mermaid/index.css", "src/templates/assets/javascripts/components/content/mermaid/index.ts", "src/templates/assets/javascripts/components/content/table/index.ts", "src/templates/assets/javascripts/components/content/tabs/index.ts", "src/templates/assets/javascripts/components/content/_/index.ts", "src/templates/assets/javascripts/components/dialog/index.ts", "src/templates/assets/javascripts/components/tooltip/index.ts", "src/templates/assets/javascripts/components/header/_/index.ts", "src/templates/assets/javascripts/components/header/title/index.ts", "src/templates/assets/javascripts/components/main/index.ts", "src/templates/assets/javascripts/components/palette/index.ts", "src/templates/assets/javascripts/components/progress/index.ts", "src/templates/assets/javascripts/integrations/sitemap/index.ts", "src/templates/assets/javascripts/integrations/alternate/index.ts", "src/templates/assets/javascripts/integrations/clipboard/index.ts", "src/templates/assets/javascripts/integrations/instant/index.ts", "src/templates/assets/javascripts/integrations/search/highlighter/index.ts", "src/templates/assets/javascripts/integrations/search/worker/message/index.ts", "src/templates/assets/javascripts/integrations/search/worker/_/index.ts", "src/templates/assets/javascripts/integrations/version/findurl/index.ts", "src/templates/assets/javascripts/integrations/version/index.ts", "src/templates/assets/javascripts/components/search/query/index.ts", "src/templates/assets/javascripts/components/search/result/index.ts", "src/templates/assets/javascripts/components/search/share/index.ts", "src/templates/assets/javascripts/components/search/suggest/index.ts", "src/templates/assets/javascripts/components/search/_/index.ts", "src/templates/assets/javascripts/components/search/highlight/index.ts", "src/templates/assets/javascripts/components/sidebar/index.ts", "src/templates/assets/javascripts/components/source/facts/github/index.ts", "src/templates/assets/javascripts/components/source/facts/gitlab/index.ts", "src/templates/assets/javascripts/components/source/facts/_/index.ts", "src/templates/assets/javascripts/components/source/_/index.ts", "src/templates/assets/javascripts/components/tabs/index.ts", "src/templates/assets/javascripts/components/toc/index.ts", "src/templates/assets/javascripts/components/top/index.ts", "src/templates/assets/javascripts/patches/ellipsis/index.ts", "src/templates/assets/javascripts/patches/indeterminate/index.ts", "src/templates/assets/javascripts/patches/scrollfix/index.ts", "src/templates/assets/javascripts/patches/scrolllock/index.ts", "src/templates/assets/javascripts/polyfills/index.ts"], + "sourcesContent": ["(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? factory() :\n typeof define === 'function' && define.amd ? define(factory) :\n (factory());\n}(this, (function () { 'use strict';\n\n /**\n * Applies the :focus-visible polyfill at the given scope.\n * A scope in this case is either the top-level Document or a Shadow Root.\n *\n * @param {(Document|ShadowRoot)} scope\n * @see https://github.com/WICG/focus-visible\n */\n function applyFocusVisiblePolyfill(scope) {\n var hadKeyboardEvent = true;\n var hadFocusVisibleRecently = false;\n var hadFocusVisibleRecentlyTimeout = null;\n\n var inputTypesAllowlist = {\n text: true,\n search: true,\n url: true,\n tel: true,\n email: true,\n password: true,\n number: true,\n date: true,\n month: true,\n week: true,\n time: true,\n datetime: true,\n 'datetime-local': true\n };\n\n /**\n * Helper function for legacy browsers and iframes which sometimes focus\n * elements like document, body, and non-interactive SVG.\n * @param {Element} el\n */\n function isValidFocusTarget(el) {\n if (\n el &&\n el !== document &&\n el.nodeName !== 'HTML' &&\n el.nodeName !== 'BODY' &&\n 'classList' in el &&\n 'contains' in el.classList\n ) {\n return true;\n }\n return false;\n }\n\n /**\n * Computes whether the given element should automatically trigger the\n * `focus-visible` class being added, i.e. whether it should always match\n * `:focus-visible` when focused.\n * @param {Element} el\n * @return {boolean}\n */\n function focusTriggersKeyboardModality(el) {\n var type = el.type;\n var tagName = el.tagName;\n\n if (tagName === 'INPUT' && inputTypesAllowlist[type] && !el.readOnly) {\n return true;\n }\n\n if (tagName === 'TEXTAREA' && !el.readOnly) {\n return true;\n }\n\n if (el.isContentEditable) {\n return true;\n }\n\n return false;\n }\n\n /**\n * Add the `focus-visible` class to the given element if it was not added by\n * the author.\n * @param {Element} el\n */\n function addFocusVisibleClass(el) {\n if (el.classList.contains('focus-visible')) {\n return;\n }\n el.classList.add('focus-visible');\n el.setAttribute('data-focus-visible-added', '');\n }\n\n /**\n * Remove the `focus-visible` class from the given element if it was not\n * originally added by the author.\n * @param {Element} el\n */\n function removeFocusVisibleClass(el) {\n if (!el.hasAttribute('data-focus-visible-added')) {\n return;\n }\n el.classList.remove('focus-visible');\n el.removeAttribute('data-focus-visible-added');\n }\n\n /**\n * If the most recent user interaction was via the keyboard;\n * and the key press did not include a meta, alt/option, or control key;\n * then the modality is keyboard. Otherwise, the modality is not keyboard.\n * Apply `focus-visible` to any current active element and keep track\n * of our keyboard modality state with `hadKeyboardEvent`.\n * @param {KeyboardEvent} e\n */\n function onKeyDown(e) {\n if (e.metaKey || e.altKey || e.ctrlKey) {\n return;\n }\n\n if (isValidFocusTarget(scope.activeElement)) {\n addFocusVisibleClass(scope.activeElement);\n }\n\n hadKeyboardEvent = true;\n }\n\n /**\n * If at any point a user clicks with a pointing device, ensure that we change\n * the modality away from keyboard.\n * This avoids the situation where a user presses a key on an already focused\n * element, and then clicks on a different element, focusing it with a\n * pointing device, while we still think we're in keyboard modality.\n * @param {Event} e\n */\n function onPointerDown(e) {\n hadKeyboardEvent = false;\n }\n\n /**\n * On `focus`, add the `focus-visible` class to the target if:\n * - the target received focus as a result of keyboard navigation, or\n * - the event target is an element that will likely require interaction\n * via the keyboard (e.g. a text box)\n * @param {Event} e\n */\n function onFocus(e) {\n // Prevent IE from focusing the document or HTML element.\n if (!isValidFocusTarget(e.target)) {\n return;\n }\n\n if (hadKeyboardEvent || focusTriggersKeyboardModality(e.target)) {\n addFocusVisibleClass(e.target);\n }\n }\n\n /**\n * On `blur`, remove the `focus-visible` class from the target.\n * @param {Event} e\n */\n function onBlur(e) {\n if (!isValidFocusTarget(e.target)) {\n return;\n }\n\n if (\n e.target.classList.contains('focus-visible') ||\n e.target.hasAttribute('data-focus-visible-added')\n ) {\n // To detect a tab/window switch, we look for a blur event followed\n // rapidly by a visibility change.\n // If we don't see a visibility change within 100ms, it's probably a\n // regular focus change.\n hadFocusVisibleRecently = true;\n window.clearTimeout(hadFocusVisibleRecentlyTimeout);\n hadFocusVisibleRecentlyTimeout = window.setTimeout(function() {\n hadFocusVisibleRecently = false;\n }, 100);\n removeFocusVisibleClass(e.target);\n }\n }\n\n /**\n * If the user changes tabs, keep track of whether or not the previously\n * focused element had .focus-visible.\n * @param {Event} e\n */\n function onVisibilityChange(e) {\n if (document.visibilityState === 'hidden') {\n // If the tab becomes active again, the browser will handle calling focus\n // on the element (Safari actually calls it twice).\n // If this tab change caused a blur on an element with focus-visible,\n // re-apply the class when the user switches back to the tab.\n if (hadFocusVisibleRecently) {\n hadKeyboardEvent = true;\n }\n addInitialPointerMoveListeners();\n }\n }\n\n /**\n * Add a group of listeners to detect usage of any pointing devices.\n * These listeners will be added when the polyfill first loads, and anytime\n * the window is blurred, so that they are active when the window regains\n * focus.\n */\n function addInitialPointerMoveListeners() {\n document.addEventListener('mousemove', onInitialPointerMove);\n document.addEventListener('mousedown', onInitialPointerMove);\n document.addEventListener('mouseup', onInitialPointerMove);\n document.addEventListener('pointermove', onInitialPointerMove);\n document.addEventListener('pointerdown', onInitialPointerMove);\n document.addEventListener('pointerup', onInitialPointerMove);\n document.addEventListener('touchmove', onInitialPointerMove);\n document.addEventListener('touchstart', onInitialPointerMove);\n document.addEventListener('touchend', onInitialPointerMove);\n }\n\n function removeInitialPointerMoveListeners() {\n document.removeEventListener('mousemove', onInitialPointerMove);\n document.removeEventListener('mousedown', onInitialPointerMove);\n document.removeEventListener('mouseup', onInitialPointerMove);\n document.removeEventListener('pointermove', onInitialPointerMove);\n document.removeEventListener('pointerdown', onInitialPointerMove);\n document.removeEventListener('pointerup', onInitialPointerMove);\n document.removeEventListener('touchmove', onInitialPointerMove);\n document.removeEventListener('touchstart', onInitialPointerMove);\n document.removeEventListener('touchend', onInitialPointerMove);\n }\n\n /**\n * When the polfyill first loads, assume the user is in keyboard modality.\n * If any event is received from a pointing device (e.g. mouse, pointer,\n * touch), turn off keyboard modality.\n * This accounts for situations where focus enters the page from the URL bar.\n * @param {Event} e\n */\n function onInitialPointerMove(e) {\n // Work around a Safari quirk that fires a mousemove on whenever the\n // window blurs, even if you're tabbing out of the page. \u00AF\\_(\u30C4)_/\u00AF\n if (e.target.nodeName && e.target.nodeName.toLowerCase() === 'html') {\n return;\n }\n\n hadKeyboardEvent = false;\n removeInitialPointerMoveListeners();\n }\n\n // For some kinds of state, we are interested in changes at the global scope\n // only. For example, global pointer input, global key presses and global\n // visibility change should affect the state at every scope:\n document.addEventListener('keydown', onKeyDown, true);\n document.addEventListener('mousedown', onPointerDown, true);\n document.addEventListener('pointerdown', onPointerDown, true);\n document.addEventListener('touchstart', onPointerDown, true);\n document.addEventListener('visibilitychange', onVisibilityChange, true);\n\n addInitialPointerMoveListeners();\n\n // For focus and blur, we specifically care about state changes in the local\n // scope. This is because focus / blur events that originate from within a\n // shadow root are not re-dispatched from the host element if it was already\n // the active element in its own scope:\n scope.addEventListener('focus', onFocus, true);\n scope.addEventListener('blur', onBlur, true);\n\n // We detect that a node is a ShadowRoot by ensuring that it is a\n // DocumentFragment and also has a host property. This check covers native\n // implementation and polyfill implementation transparently. If we only cared\n // about the native implementation, we could just check if the scope was\n // an instance of a ShadowRoot.\n if (scope.nodeType === Node.DOCUMENT_FRAGMENT_NODE && scope.host) {\n // Since a ShadowRoot is a special kind of DocumentFragment, it does not\n // have a root element to add a class to. So, we add this attribute to the\n // host element instead:\n scope.host.setAttribute('data-js-focus-visible', '');\n } else if (scope.nodeType === Node.DOCUMENT_NODE) {\n document.documentElement.classList.add('js-focus-visible');\n document.documentElement.setAttribute('data-js-focus-visible', '');\n }\n }\n\n // It is important to wrap all references to global window and document in\n // these checks to support server-side rendering use cases\n // @see https://github.com/WICG/focus-visible/issues/199\n if (typeof window !== 'undefined' && typeof document !== 'undefined') {\n // Make the polyfill helper globally available. This can be used as a signal\n // to interested libraries that wish to coordinate with the polyfill for e.g.,\n // applying the polyfill to a shadow root:\n window.applyFocusVisiblePolyfill = applyFocusVisiblePolyfill;\n\n // Notify interested libraries of the polyfill's presence, in case the\n // polyfill was loaded lazily:\n var event;\n\n try {\n event = new CustomEvent('focus-visible-polyfill-ready');\n } catch (error) {\n // IE11 does not support using CustomEvent as a constructor directly:\n event = document.createEvent('CustomEvent');\n event.initCustomEvent('focus-visible-polyfill-ready', false, false, {});\n }\n\n window.dispatchEvent(event);\n }\n\n if (typeof document !== 'undefined') {\n // Apply the polyfill to the global document, so that no JavaScript\n // coordination is required to use the polyfill in the top-level document:\n applyFocusVisiblePolyfill(document);\n }\n\n})));\n", "/*!\n * escape-html\n * Copyright(c) 2012-2013 TJ Holowaychuk\n * Copyright(c) 2015 Andreas Lubbe\n * Copyright(c) 2015 Tiancheng \"Timothy\" Gu\n * MIT Licensed\n */\n\n'use strict';\n\n/**\n * Module variables.\n * @private\n */\n\nvar matchHtmlRegExp = /[\"'&<>]/;\n\n/**\n * Module exports.\n * @public\n */\n\nmodule.exports = escapeHtml;\n\n/**\n * Escape special characters in the given string of html.\n *\n * @param {string} string The string to escape for inserting into HTML\n * @return {string}\n * @public\n */\n\nfunction escapeHtml(string) {\n var str = '' + string;\n var match = matchHtmlRegExp.exec(str);\n\n if (!match) {\n return str;\n }\n\n var escape;\n var html = '';\n var index = 0;\n var lastIndex = 0;\n\n for (index = match.index; index < str.length; index++) {\n switch (str.charCodeAt(index)) {\n case 34: // \"\n escape = '"';\n break;\n case 38: // &\n escape = '&';\n break;\n case 39: // '\n escape = ''';\n break;\n case 60: // <\n escape = '<';\n break;\n case 62: // >\n escape = '>';\n break;\n default:\n continue;\n }\n\n if (lastIndex !== index) {\n html += str.substring(lastIndex, index);\n }\n\n lastIndex = index + 1;\n html += escape;\n }\n\n return lastIndex !== index\n ? html + str.substring(lastIndex, index)\n : html;\n}\n", "/*!\n * clipboard.js v2.0.11\n * https://clipboardjs.com/\n *\n * Licensed MIT \u00A9 Zeno Rocha\n */\n(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"ClipboardJS\"] = factory();\n\telse\n\t\troot[\"ClipboardJS\"] = factory();\n})(this, function() {\nreturn /******/ (function() { // webpackBootstrap\n/******/ \tvar __webpack_modules__ = ({\n\n/***/ 686:\n/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n\n// EXPORTS\n__webpack_require__.d(__webpack_exports__, {\n \"default\": function() { return /* binding */ clipboard; }\n});\n\n// EXTERNAL MODULE: ./node_modules/tiny-emitter/index.js\nvar tiny_emitter = __webpack_require__(279);\nvar tiny_emitter_default = /*#__PURE__*/__webpack_require__.n(tiny_emitter);\n// EXTERNAL MODULE: ./node_modules/good-listener/src/listen.js\nvar listen = __webpack_require__(370);\nvar listen_default = /*#__PURE__*/__webpack_require__.n(listen);\n// EXTERNAL MODULE: ./node_modules/select/src/select.js\nvar src_select = __webpack_require__(817);\nvar select_default = /*#__PURE__*/__webpack_require__.n(src_select);\n;// CONCATENATED MODULE: ./src/common/command.js\n/**\n * Executes a given operation type.\n * @param {String} type\n * @return {Boolean}\n */\nfunction command(type) {\n try {\n return document.execCommand(type);\n } catch (err) {\n return false;\n }\n}\n;// CONCATENATED MODULE: ./src/actions/cut.js\n\n\n/**\n * Cut action wrapper.\n * @param {String|HTMLElement} target\n * @return {String}\n */\n\nvar ClipboardActionCut = function ClipboardActionCut(target) {\n var selectedText = select_default()(target);\n command('cut');\n return selectedText;\n};\n\n/* harmony default export */ var actions_cut = (ClipboardActionCut);\n;// CONCATENATED MODULE: ./src/common/create-fake-element.js\n/**\n * Creates a fake textarea element with a value.\n * @param {String} value\n * @return {HTMLElement}\n */\nfunction createFakeElement(value) {\n var isRTL = document.documentElement.getAttribute('dir') === 'rtl';\n var fakeElement = document.createElement('textarea'); // Prevent zooming on iOS\n\n fakeElement.style.fontSize = '12pt'; // Reset box model\n\n fakeElement.style.border = '0';\n fakeElement.style.padding = '0';\n fakeElement.style.margin = '0'; // Move element out of screen horizontally\n\n fakeElement.style.position = 'absolute';\n fakeElement.style[isRTL ? 'right' : 'left'] = '-9999px'; // Move element to the same position vertically\n\n var yPosition = window.pageYOffset || document.documentElement.scrollTop;\n fakeElement.style.top = \"\".concat(yPosition, \"px\");\n fakeElement.setAttribute('readonly', '');\n fakeElement.value = value;\n return fakeElement;\n}\n;// CONCATENATED MODULE: ./src/actions/copy.js\n\n\n\n/**\n * Create fake copy action wrapper using a fake element.\n * @param {String} target\n * @param {Object} options\n * @return {String}\n */\n\nvar fakeCopyAction = function fakeCopyAction(value, options) {\n var fakeElement = createFakeElement(value);\n options.container.appendChild(fakeElement);\n var selectedText = select_default()(fakeElement);\n command('copy');\n fakeElement.remove();\n return selectedText;\n};\n/**\n * Copy action wrapper.\n * @param {String|HTMLElement} target\n * @param {Object} options\n * @return {String}\n */\n\n\nvar ClipboardActionCopy = function ClipboardActionCopy(target) {\n var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {\n container: document.body\n };\n var selectedText = '';\n\n if (typeof target === 'string') {\n selectedText = fakeCopyAction(target, options);\n } else if (target instanceof HTMLInputElement && !['text', 'search', 'url', 'tel', 'password'].includes(target === null || target === void 0 ? void 0 : target.type)) {\n // If input type doesn't support `setSelectionRange`. Simulate it. https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/setSelectionRange\n selectedText = fakeCopyAction(target.value, options);\n } else {\n selectedText = select_default()(target);\n command('copy');\n }\n\n return selectedText;\n};\n\n/* harmony default export */ var actions_copy = (ClipboardActionCopy);\n;// CONCATENATED MODULE: ./src/actions/default.js\nfunction _typeof(obj) { \"@babel/helpers - typeof\"; if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return _typeof(obj); }\n\n\n\n/**\n * Inner function which performs selection from either `text` or `target`\n * properties and then executes copy or cut operations.\n * @param {Object} options\n */\n\nvar ClipboardActionDefault = function ClipboardActionDefault() {\n var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n // Defines base properties passed from constructor.\n var _options$action = options.action,\n action = _options$action === void 0 ? 'copy' : _options$action,\n container = options.container,\n target = options.target,\n text = options.text; // Sets the `action` to be performed which can be either 'copy' or 'cut'.\n\n if (action !== 'copy' && action !== 'cut') {\n throw new Error('Invalid \"action\" value, use either \"copy\" or \"cut\"');\n } // Sets the `target` property using an element that will be have its content copied.\n\n\n if (target !== undefined) {\n if (target && _typeof(target) === 'object' && target.nodeType === 1) {\n if (action === 'copy' && target.hasAttribute('disabled')) {\n throw new Error('Invalid \"target\" attribute. Please use \"readonly\" instead of \"disabled\" attribute');\n }\n\n if (action === 'cut' && (target.hasAttribute('readonly') || target.hasAttribute('disabled'))) {\n throw new Error('Invalid \"target\" attribute. You can\\'t cut text from elements with \"readonly\" or \"disabled\" attributes');\n }\n } else {\n throw new Error('Invalid \"target\" value, use a valid Element');\n }\n } // Define selection strategy based on `text` property.\n\n\n if (text) {\n return actions_copy(text, {\n container: container\n });\n } // Defines which selection strategy based on `target` property.\n\n\n if (target) {\n return action === 'cut' ? actions_cut(target) : actions_copy(target, {\n container: container\n });\n }\n};\n\n/* harmony default export */ var actions_default = (ClipboardActionDefault);\n;// CONCATENATED MODULE: ./src/clipboard.js\nfunction clipboard_typeof(obj) { \"@babel/helpers - typeof\"; if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { clipboard_typeof = function _typeof(obj) { return typeof obj; }; } else { clipboard_typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return clipboard_typeof(obj); }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function\"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }\n\nfunction _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }\n\nfunction _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }\n\nfunction _possibleConstructorReturn(self, call) { if (call && (clipboard_typeof(call) === \"object\" || typeof call === \"function\")) { return call; } return _assertThisInitialized(self); }\n\nfunction _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return self; }\n\nfunction _isNativeReflectConstruct() { if (typeof Reflect === \"undefined\" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === \"function\") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }\n\nfunction _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }\n\n\n\n\n\n\n/**\n * Helper function to retrieve attribute value.\n * @param {String} suffix\n * @param {Element} element\n */\n\nfunction getAttributeValue(suffix, element) {\n var attribute = \"data-clipboard-\".concat(suffix);\n\n if (!element.hasAttribute(attribute)) {\n return;\n }\n\n return element.getAttribute(attribute);\n}\n/**\n * Base class which takes one or more elements, adds event listeners to them,\n * and instantiates a new `ClipboardAction` on each click.\n */\n\n\nvar Clipboard = /*#__PURE__*/function (_Emitter) {\n _inherits(Clipboard, _Emitter);\n\n var _super = _createSuper(Clipboard);\n\n /**\n * @param {String|HTMLElement|HTMLCollection|NodeList} trigger\n * @param {Object} options\n */\n function Clipboard(trigger, options) {\n var _this;\n\n _classCallCheck(this, Clipboard);\n\n _this = _super.call(this);\n\n _this.resolveOptions(options);\n\n _this.listenClick(trigger);\n\n return _this;\n }\n /**\n * Defines if attributes would be resolved using internal setter functions\n * or custom functions that were passed in the constructor.\n * @param {Object} options\n */\n\n\n _createClass(Clipboard, [{\n key: \"resolveOptions\",\n value: function resolveOptions() {\n var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n this.action = typeof options.action === 'function' ? options.action : this.defaultAction;\n this.target = typeof options.target === 'function' ? options.target : this.defaultTarget;\n this.text = typeof options.text === 'function' ? options.text : this.defaultText;\n this.container = clipboard_typeof(options.container) === 'object' ? options.container : document.body;\n }\n /**\n * Adds a click event listener to the passed trigger.\n * @param {String|HTMLElement|HTMLCollection|NodeList} trigger\n */\n\n }, {\n key: \"listenClick\",\n value: function listenClick(trigger) {\n var _this2 = this;\n\n this.listener = listen_default()(trigger, 'click', function (e) {\n return _this2.onClick(e);\n });\n }\n /**\n * Defines a new `ClipboardAction` on each click event.\n * @param {Event} e\n */\n\n }, {\n key: \"onClick\",\n value: function onClick(e) {\n var trigger = e.delegateTarget || e.currentTarget;\n var action = this.action(trigger) || 'copy';\n var text = actions_default({\n action: action,\n container: this.container,\n target: this.target(trigger),\n text: this.text(trigger)\n }); // Fires an event based on the copy operation result.\n\n this.emit(text ? 'success' : 'error', {\n action: action,\n text: text,\n trigger: trigger,\n clearSelection: function clearSelection() {\n if (trigger) {\n trigger.focus();\n }\n\n window.getSelection().removeAllRanges();\n }\n });\n }\n /**\n * Default `action` lookup function.\n * @param {Element} trigger\n */\n\n }, {\n key: \"defaultAction\",\n value: function defaultAction(trigger) {\n return getAttributeValue('action', trigger);\n }\n /**\n * Default `target` lookup function.\n * @param {Element} trigger\n */\n\n }, {\n key: \"defaultTarget\",\n value: function defaultTarget(trigger) {\n var selector = getAttributeValue('target', trigger);\n\n if (selector) {\n return document.querySelector(selector);\n }\n }\n /**\n * Allow fire programmatically a copy action\n * @param {String|HTMLElement} target\n * @param {Object} options\n * @returns Text copied.\n */\n\n }, {\n key: \"defaultText\",\n\n /**\n * Default `text` lookup function.\n * @param {Element} trigger\n */\n value: function defaultText(trigger) {\n return getAttributeValue('text', trigger);\n }\n /**\n * Destroy lifecycle.\n */\n\n }, {\n key: \"destroy\",\n value: function destroy() {\n this.listener.destroy();\n }\n }], [{\n key: \"copy\",\n value: function copy(target) {\n var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {\n container: document.body\n };\n return actions_copy(target, options);\n }\n /**\n * Allow fire programmatically a cut action\n * @param {String|HTMLElement} target\n * @returns Text cutted.\n */\n\n }, {\n key: \"cut\",\n value: function cut(target) {\n return actions_cut(target);\n }\n /**\n * Returns the support of the given action, or all actions if no action is\n * given.\n * @param {String} [action]\n */\n\n }, {\n key: \"isSupported\",\n value: function isSupported() {\n var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ['copy', 'cut'];\n var actions = typeof action === 'string' ? [action] : action;\n var support = !!document.queryCommandSupported;\n actions.forEach(function (action) {\n support = support && !!document.queryCommandSupported(action);\n });\n return support;\n }\n }]);\n\n return Clipboard;\n}((tiny_emitter_default()));\n\n/* harmony default export */ var clipboard = (Clipboard);\n\n/***/ }),\n\n/***/ 828:\n/***/ (function(module) {\n\nvar DOCUMENT_NODE_TYPE = 9;\n\n/**\n * A polyfill for Element.matches()\n */\nif (typeof Element !== 'undefined' && !Element.prototype.matches) {\n var proto = Element.prototype;\n\n proto.matches = proto.matchesSelector ||\n proto.mozMatchesSelector ||\n proto.msMatchesSelector ||\n proto.oMatchesSelector ||\n proto.webkitMatchesSelector;\n}\n\n/**\n * Finds the closest parent that matches a selector.\n *\n * @param {Element} element\n * @param {String} selector\n * @return {Function}\n */\nfunction closest (element, selector) {\n while (element && element.nodeType !== DOCUMENT_NODE_TYPE) {\n if (typeof element.matches === 'function' &&\n element.matches(selector)) {\n return element;\n }\n element = element.parentNode;\n }\n}\n\nmodule.exports = closest;\n\n\n/***/ }),\n\n/***/ 438:\n/***/ (function(module, __unused_webpack_exports, __webpack_require__) {\n\nvar closest = __webpack_require__(828);\n\n/**\n * Delegates event to a selector.\n *\n * @param {Element} element\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @param {Boolean} useCapture\n * @return {Object}\n */\nfunction _delegate(element, selector, type, callback, useCapture) {\n var listenerFn = listener.apply(this, arguments);\n\n element.addEventListener(type, listenerFn, useCapture);\n\n return {\n destroy: function() {\n element.removeEventListener(type, listenerFn, useCapture);\n }\n }\n}\n\n/**\n * Delegates event to a selector.\n *\n * @param {Element|String|Array} [elements]\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @param {Boolean} useCapture\n * @return {Object}\n */\nfunction delegate(elements, selector, type, callback, useCapture) {\n // Handle the regular Element usage\n if (typeof elements.addEventListener === 'function') {\n return _delegate.apply(null, arguments);\n }\n\n // Handle Element-less usage, it defaults to global delegation\n if (typeof type === 'function') {\n // Use `document` as the first parameter, then apply arguments\n // This is a short way to .unshift `arguments` without running into deoptimizations\n return _delegate.bind(null, document).apply(null, arguments);\n }\n\n // Handle Selector-based usage\n if (typeof elements === 'string') {\n elements = document.querySelectorAll(elements);\n }\n\n // Handle Array-like based usage\n return Array.prototype.map.call(elements, function (element) {\n return _delegate(element, selector, type, callback, useCapture);\n });\n}\n\n/**\n * Finds closest match and invokes callback.\n *\n * @param {Element} element\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @return {Function}\n */\nfunction listener(element, selector, type, callback) {\n return function(e) {\n e.delegateTarget = closest(e.target, selector);\n\n if (e.delegateTarget) {\n callback.call(element, e);\n }\n }\n}\n\nmodule.exports = delegate;\n\n\n/***/ }),\n\n/***/ 879:\n/***/ (function(__unused_webpack_module, exports) {\n\n/**\n * Check if argument is a HTML element.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.node = function(value) {\n return value !== undefined\n && value instanceof HTMLElement\n && value.nodeType === 1;\n};\n\n/**\n * Check if argument is a list of HTML elements.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.nodeList = function(value) {\n var type = Object.prototype.toString.call(value);\n\n return value !== undefined\n && (type === '[object NodeList]' || type === '[object HTMLCollection]')\n && ('length' in value)\n && (value.length === 0 || exports.node(value[0]));\n};\n\n/**\n * Check if argument is a string.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.string = function(value) {\n return typeof value === 'string'\n || value instanceof String;\n};\n\n/**\n * Check if argument is a function.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.fn = function(value) {\n var type = Object.prototype.toString.call(value);\n\n return type === '[object Function]';\n};\n\n\n/***/ }),\n\n/***/ 370:\n/***/ (function(module, __unused_webpack_exports, __webpack_require__) {\n\nvar is = __webpack_require__(879);\nvar delegate = __webpack_require__(438);\n\n/**\n * Validates all params and calls the right\n * listener function based on its target type.\n *\n * @param {String|HTMLElement|HTMLCollection|NodeList} target\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listen(target, type, callback) {\n if (!target && !type && !callback) {\n throw new Error('Missing required arguments');\n }\n\n if (!is.string(type)) {\n throw new TypeError('Second argument must be a String');\n }\n\n if (!is.fn(callback)) {\n throw new TypeError('Third argument must be a Function');\n }\n\n if (is.node(target)) {\n return listenNode(target, type, callback);\n }\n else if (is.nodeList(target)) {\n return listenNodeList(target, type, callback);\n }\n else if (is.string(target)) {\n return listenSelector(target, type, callback);\n }\n else {\n throw new TypeError('First argument must be a String, HTMLElement, HTMLCollection, or NodeList');\n }\n}\n\n/**\n * Adds an event listener to a HTML element\n * and returns a remove listener function.\n *\n * @param {HTMLElement} node\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenNode(node, type, callback) {\n node.addEventListener(type, callback);\n\n return {\n destroy: function() {\n node.removeEventListener(type, callback);\n }\n }\n}\n\n/**\n * Add an event listener to a list of HTML elements\n * and returns a remove listener function.\n *\n * @param {NodeList|HTMLCollection} nodeList\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenNodeList(nodeList, type, callback) {\n Array.prototype.forEach.call(nodeList, function(node) {\n node.addEventListener(type, callback);\n });\n\n return {\n destroy: function() {\n Array.prototype.forEach.call(nodeList, function(node) {\n node.removeEventListener(type, callback);\n });\n }\n }\n}\n\n/**\n * Add an event listener to a selector\n * and returns a remove listener function.\n *\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenSelector(selector, type, callback) {\n return delegate(document.body, selector, type, callback);\n}\n\nmodule.exports = listen;\n\n\n/***/ }),\n\n/***/ 817:\n/***/ (function(module) {\n\nfunction select(element) {\n var selectedText;\n\n if (element.nodeName === 'SELECT') {\n element.focus();\n\n selectedText = element.value;\n }\n else if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') {\n var isReadOnly = element.hasAttribute('readonly');\n\n if (!isReadOnly) {\n element.setAttribute('readonly', '');\n }\n\n element.select();\n element.setSelectionRange(0, element.value.length);\n\n if (!isReadOnly) {\n element.removeAttribute('readonly');\n }\n\n selectedText = element.value;\n }\n else {\n if (element.hasAttribute('contenteditable')) {\n element.focus();\n }\n\n var selection = window.getSelection();\n var range = document.createRange();\n\n range.selectNodeContents(element);\n selection.removeAllRanges();\n selection.addRange(range);\n\n selectedText = selection.toString();\n }\n\n return selectedText;\n}\n\nmodule.exports = select;\n\n\n/***/ }),\n\n/***/ 279:\n/***/ (function(module) {\n\nfunction E () {\n // Keep this empty so it's easier to inherit from\n // (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3)\n}\n\nE.prototype = {\n on: function (name, callback, ctx) {\n var e = this.e || (this.e = {});\n\n (e[name] || (e[name] = [])).push({\n fn: callback,\n ctx: ctx\n });\n\n return this;\n },\n\n once: function (name, callback, ctx) {\n var self = this;\n function listener () {\n self.off(name, listener);\n callback.apply(ctx, arguments);\n };\n\n listener._ = callback\n return this.on(name, listener, ctx);\n },\n\n emit: function (name) {\n var data = [].slice.call(arguments, 1);\n var evtArr = ((this.e || (this.e = {}))[name] || []).slice();\n var i = 0;\n var len = evtArr.length;\n\n for (i; i < len; i++) {\n evtArr[i].fn.apply(evtArr[i].ctx, data);\n }\n\n return this;\n },\n\n off: function (name, callback) {\n var e = this.e || (this.e = {});\n var evts = e[name];\n var liveEvents = [];\n\n if (evts && callback) {\n for (var i = 0, len = evts.length; i < len; i++) {\n if (evts[i].fn !== callback && evts[i].fn._ !== callback)\n liveEvents.push(evts[i]);\n }\n }\n\n // Remove event from queue to prevent memory leak\n // Suggested by https://github.com/lazd\n // Ref: https://github.com/scottcorgan/tiny-emitter/commit/c6ebfaa9bc973b33d110a84a307742b7cf94c953#commitcomment-5024910\n\n (liveEvents.length)\n ? e[name] = liveEvents\n : delete e[name];\n\n return this;\n }\n};\n\nmodule.exports = E;\nmodule.exports.TinyEmitter = E;\n\n\n/***/ })\n\n/******/ \t});\n/************************************************************************/\n/******/ \t// The module cache\n/******/ \tvar __webpack_module_cache__ = {};\n/******/ \t\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(__webpack_module_cache__[moduleId]) {\n/******/ \t\t\treturn __webpack_module_cache__[moduleId].exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = __webpack_module_cache__[moduleId] = {\n/******/ \t\t\t// no module.id needed\n/******/ \t\t\t// no module.loaded needed\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/ \t\n/******/ \t\t// Execute the module function\n/******/ \t\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n/******/ \t\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/ \t\n/************************************************************************/\n/******/ \t/* webpack/runtime/compat get default export */\n/******/ \t!function() {\n/******/ \t\t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t\t__webpack_require__.n = function(module) {\n/******/ \t\t\tvar getter = module && module.__esModule ?\n/******/ \t\t\t\tfunction() { return module['default']; } :\n/******/ \t\t\t\tfunction() { return module; };\n/******/ \t\t\t__webpack_require__.d(getter, { a: getter });\n/******/ \t\t\treturn getter;\n/******/ \t\t};\n/******/ \t}();\n/******/ \t\n/******/ \t/* webpack/runtime/define property getters */\n/******/ \t!function() {\n/******/ \t\t// define getter functions for harmony exports\n/******/ \t\t__webpack_require__.d = function(exports, definition) {\n/******/ \t\t\tfor(var key in definition) {\n/******/ \t\t\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n/******/ \t\t\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n/******/ \t\t\t\t}\n/******/ \t\t\t}\n/******/ \t\t};\n/******/ \t}();\n/******/ \t\n/******/ \t/* webpack/runtime/hasOwnProperty shorthand */\n/******/ \t!function() {\n/******/ \t\t__webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }\n/******/ \t}();\n/******/ \t\n/************************************************************************/\n/******/ \t// module exports must be returned from runtime so entry inlining is disabled\n/******/ \t// startup\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(686);\n/******/ })()\n.default;\n});", "/*\n * Copyright (c) 2016-2025 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport \"focus-visible\"\n\nimport {\n EMPTY,\n NEVER,\n Observable,\n Subject,\n defer,\n delay,\n filter,\n map,\n merge,\n mergeWith,\n shareReplay,\n switchMap\n} from \"rxjs\"\n\nimport { configuration, feature } from \"./_\"\nimport {\n at,\n getActiveElement,\n getOptionalElement,\n requestJSON,\n setLocation,\n setToggle,\n watchDocument,\n watchKeyboard,\n watchLocation,\n watchLocationTarget,\n watchMedia,\n watchPrint,\n watchScript,\n watchViewport\n} from \"./browser\"\nimport {\n getComponentElement,\n getComponentElements,\n mountAnnounce,\n mountBackToTop,\n mountConsent,\n mountContent,\n mountDialog,\n mountHeader,\n mountHeaderTitle,\n mountPalette,\n mountProgress,\n mountSearch,\n mountSearchHiglight,\n mountSidebar,\n mountSource,\n mountTableOfContents,\n mountTabs,\n watchHeader,\n watchMain\n} from \"./components\"\nimport {\n SearchIndex,\n fetchSitemap,\n setupAlternate,\n setupClipboardJS,\n setupInstantNavigation,\n setupVersionSelector\n} from \"./integrations\"\nimport {\n patchEllipsis,\n patchIndeterminate,\n patchScrollfix,\n patchScrolllock\n} from \"./patches\"\nimport \"./polyfills\"\n\n/* ----------------------------------------------------------------------------\n * Functions - @todo refactor\n * ------------------------------------------------------------------------- */\n\n/**\n * Fetch search index\n *\n * @returns Search index observable\n */\nfunction fetchSearchIndex(): Observable {\n if (location.protocol === \"file:\") {\n return watchScript(\n `${new URL(\"search/search_index.js\", config.base)}`\n )\n .pipe(\n // @ts-ignore - @todo fix typings\n map(() => __index),\n shareReplay(1)\n )\n } else {\n return requestJSON(\n new URL(\"search/search_index.json\", config.base)\n )\n }\n}\n\n/* ----------------------------------------------------------------------------\n * Application\n * ------------------------------------------------------------------------- */\n\n/* Yay, JavaScript is available */\ndocument.documentElement.classList.remove(\"no-js\")\ndocument.documentElement.classList.add(\"js\")\n\n/* Set up navigation observables and subjects */\nconst document$ = watchDocument()\nconst location$ = watchLocation()\nconst target$ = watchLocationTarget(location$)\nconst keyboard$ = watchKeyboard()\n\n/* Set up media observables */\nconst viewport$ = watchViewport()\nconst tablet$ = watchMedia(\"(min-width: 60em)\")\nconst screen$ = watchMedia(\"(min-width: 76.25em)\")\nconst print$ = watchPrint()\n\n/* Retrieve search index, if search is enabled */\nconst config = configuration()\nconst index$ = document.forms.namedItem(\"search\")\n ? fetchSearchIndex()\n : NEVER\n\n/* Set up Clipboard.js integration */\nconst alert$ = new Subject()\nsetupClipboardJS({ alert$ })\n\n/* Set up language selector */\nsetupAlternate({ document$ })\n\n/* Set up progress indicator */\nconst progress$ = new Subject()\n\n/* Set up sitemap for instant navigation and previews */\nconst sitemap$ = fetchSitemap(config.base)\n\n/* Set up instant navigation, if enabled */\nif (feature(\"navigation.instant\"))\n setupInstantNavigation({ sitemap$, location$, viewport$, progress$ })\n .subscribe(document$)\n\n/* Set up version selector */\nif (config.version?.provider === \"mike\")\n setupVersionSelector({ document$ })\n\n/* Always close drawer and search on navigation */\nmerge(location$, target$)\n .pipe(\n delay(125)\n )\n .subscribe(() => {\n setToggle(\"drawer\", false)\n setToggle(\"search\", false)\n })\n\n/* Set up global keyboard handlers */\nkeyboard$\n .pipe(\n filter(({ mode }) => mode === \"global\")\n )\n .subscribe(key => {\n switch (key.type) {\n\n /* Go to previous page */\n case \"p\":\n case \",\":\n const prev = getOptionalElement(\"link[rel=prev]\")\n if (typeof prev !== \"undefined\")\n setLocation(prev)\n break\n\n /* Go to next page */\n case \"n\":\n case \".\":\n const next = getOptionalElement(\"link[rel=next]\")\n if (typeof next !== \"undefined\")\n setLocation(next)\n break\n\n /* Expand navigation, see https://bit.ly/3ZjG5io */\n case \"Enter\":\n const active = getActiveElement()\n if (active instanceof HTMLLabelElement)\n active.click()\n }\n })\n\n/* Set up patches */\npatchEllipsis({ viewport$, document$ })\npatchIndeterminate({ document$, tablet$ })\npatchScrollfix({ document$ })\npatchScrolllock({ viewport$, tablet$ })\n\n/* Set up header and main area observable */\nconst header$ = watchHeader(getComponentElement(\"header\"), { viewport$ })\nconst main$ = document$\n .pipe(\n map(() => getComponentElement(\"main\")),\n switchMap(el => watchMain(el, { viewport$, header$ })),\n shareReplay(1)\n )\n\n/* Set up control component observables */\nconst control$ = merge(\n\n /* Consent */\n ...getComponentElements(\"consent\")\n .map(el => mountConsent(el, { target$ })),\n\n /* Dialog */\n ...getComponentElements(\"dialog\")\n .map(el => mountDialog(el, { alert$ })),\n\n /* Color palette */\n ...getComponentElements(\"palette\")\n .map(el => mountPalette(el)),\n\n /* Progress bar */\n ...getComponentElements(\"progress\")\n .map(el => mountProgress(el, { progress$ })),\n\n /* Search */\n ...getComponentElements(\"search\")\n .map(el => mountSearch(el, { index$, keyboard$ })),\n\n /* Repository information */\n ...getComponentElements(\"source\")\n .map(el => mountSource(el))\n)\n\n/* Set up content component observables */\nconst content$ = defer(() => merge(\n\n /* Announcement bar */\n ...getComponentElements(\"announce\")\n .map(el => mountAnnounce(el)),\n\n /* Content */\n ...getComponentElements(\"content\")\n .map(el => mountContent(el, { sitemap$, viewport$, target$, print$ })),\n\n /* Search highlighting */\n ...getComponentElements(\"content\")\n .map(el => feature(\"search.highlight\")\n ? mountSearchHiglight(el, { index$, location$ })\n : EMPTY\n ),\n\n /* Header */\n ...getComponentElements(\"header\")\n .map(el => mountHeader(el, { viewport$, header$, main$ })),\n\n /* Header title */\n ...getComponentElements(\"header-title\")\n .map(el => mountHeaderTitle(el, { viewport$, header$ })),\n\n /* Sidebar */\n ...getComponentElements(\"sidebar\")\n .map(el => el.getAttribute(\"data-md-type\") === \"navigation\"\n ? at(screen$, () => mountSidebar(el, { viewport$, header$, main$ }))\n : at(tablet$, () => mountSidebar(el, { viewport$, header$, main$ }))\n ),\n\n /* Navigation tabs */\n ...getComponentElements(\"tabs\")\n .map(el => mountTabs(el, { viewport$, header$ })),\n\n /* Table of contents */\n ...getComponentElements(\"toc\")\n .map(el => mountTableOfContents(el, {\n viewport$, header$, main$, target$\n })),\n\n /* Back-to-top button */\n ...getComponentElements(\"top\")\n .map(el => mountBackToTop(el, { viewport$, header$, main$, target$ }))\n))\n\n/* Set up component observables */\nconst component$ = document$\n .pipe(\n switchMap(() => content$),\n mergeWith(control$),\n shareReplay(1)\n )\n\n/* Subscribe to all components */\ncomponent$.subscribe()\n\n/* ----------------------------------------------------------------------------\n * Exports\n * ------------------------------------------------------------------------- */\n\nwindow.document$ = document$ /* Document observable */\nwindow.location$ = location$ /* Location subject */\nwindow.target$ = target$ /* Location target observable */\nwindow.keyboard$ = keyboard$ /* Keyboard observable */\nwindow.viewport$ = viewport$ /* Viewport observable */\nwindow.tablet$ = tablet$ /* Media tablet observable */\nwindow.screen$ = screen$ /* Media screen observable */\nwindow.print$ = print$ /* Media print observable */\nwindow.alert$ = alert$ /* Alert subject */\nwindow.progress$ = progress$ /* Progress indicator subject */\nwindow.component$ = component$ /* Component observable */\n", "/******************************************************************************\nCopyright (c) Microsoft Corporation.\n\nPermission to use, copy, modify, and/or distribute this software for any\npurpose with or without fee is hereby granted.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\nREGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY\nAND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\nINDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\nLOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\nOTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\nPERFORMANCE OF THIS SOFTWARE.\n***************************************************************************** */\n/* global Reflect, Promise, SuppressedError, Symbol, Iterator */\n\nvar extendStatics = function(d, b) {\n extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };\n return extendStatics(d, b);\n};\n\nexport function __extends(d, b) {\n if (typeof b !== \"function\" && b !== null)\n throw new TypeError(\"Class extends value \" + String(b) + \" is not a constructor or null\");\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n}\n\nexport var __assign = function() {\n __assign = Object.assign || function __assign(t) {\n for (var s, i = 1, n = arguments.length; i < n; i++) {\n s = arguments[i];\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];\n }\n return t;\n }\n return __assign.apply(this, arguments);\n}\n\nexport function __rest(s, e) {\n var t = {};\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)\n t[p] = s[p];\n if (s != null && typeof Object.getOwnPropertySymbols === \"function\")\n for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {\n if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))\n t[p[i]] = s[p[i]];\n }\n return t;\n}\n\nexport function __decorate(decorators, target, key, desc) {\n var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;\n if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\") r = Reflect.decorate(decorators, target, key, desc);\n else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\n return c > 3 && r && Object.defineProperty(target, key, r), r;\n}\n\nexport function __param(paramIndex, decorator) {\n return function (target, key) { decorator(target, key, paramIndex); }\n}\n\nexport function __esDecorate(ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {\n function accept(f) { if (f !== void 0 && typeof f !== \"function\") throw new TypeError(\"Function expected\"); return f; }\n var kind = contextIn.kind, key = kind === \"getter\" ? \"get\" : kind === \"setter\" ? \"set\" : \"value\";\n var target = !descriptorIn && ctor ? contextIn[\"static\"] ? ctor : ctor.prototype : null;\n var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});\n var _, done = false;\n for (var i = decorators.length - 1; i >= 0; i--) {\n var context = {};\n for (var p in contextIn) context[p] = p === \"access\" ? {} : contextIn[p];\n for (var p in contextIn.access) context.access[p] = contextIn.access[p];\n context.addInitializer = function (f) { if (done) throw new TypeError(\"Cannot add initializers after decoration has completed\"); extraInitializers.push(accept(f || null)); };\n var result = (0, decorators[i])(kind === \"accessor\" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);\n if (kind === \"accessor\") {\n if (result === void 0) continue;\n if (result === null || typeof result !== \"object\") throw new TypeError(\"Object expected\");\n if (_ = accept(result.get)) descriptor.get = _;\n if (_ = accept(result.set)) descriptor.set = _;\n if (_ = accept(result.init)) initializers.unshift(_);\n }\n else if (_ = accept(result)) {\n if (kind === \"field\") initializers.unshift(_);\n else descriptor[key] = _;\n }\n }\n if (target) Object.defineProperty(target, contextIn.name, descriptor);\n done = true;\n};\n\nexport function __runInitializers(thisArg, initializers, value) {\n var useValue = arguments.length > 2;\n for (var i = 0; i < initializers.length; i++) {\n value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);\n }\n return useValue ? value : void 0;\n};\n\nexport function __propKey(x) {\n return typeof x === \"symbol\" ? x : \"\".concat(x);\n};\n\nexport function __setFunctionName(f, name, prefix) {\n if (typeof name === \"symbol\") name = name.description ? \"[\".concat(name.description, \"]\") : \"\";\n return Object.defineProperty(f, \"name\", { configurable: true, value: prefix ? \"\".concat(prefix, \" \", name) : name });\n};\n\nexport function __metadata(metadataKey, metadataValue) {\n if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\") return Reflect.metadata(metadataKey, metadataValue);\n}\n\nexport function __awaiter(thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n}\n\nexport function __generator(thisArg, body) {\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === \"function\" ? Iterator : Object).prototype);\n return g.next = verb(0), g[\"throw\"] = verb(1), g[\"return\"] = verb(2), typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\n function verb(n) { return function (v) { return step([n, v]); }; }\n function step(op) {\n if (f) throw new TypeError(\"Generator is already executing.\");\n while (g && (g = 0, op[0] && (_ = 0)), _) try {\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\n if (y = 0, t) op = [op[0] & 2, t.value];\n switch (op[0]) {\n case 0: case 1: t = op; break;\n case 4: _.label++; return { value: op[1], done: false };\n case 5: _.label++; y = op[1]; op = [0]; continue;\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\n default:\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\n if (t[2]) _.ops.pop();\n _.trys.pop(); continue;\n }\n op = body.call(thisArg, _);\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\n }\n}\n\nexport var __createBinding = Object.create ? (function(o, m, k, k2) {\n if (k2 === undefined) k2 = k;\n var desc = Object.getOwnPropertyDescriptor(m, k);\n if (!desc || (\"get\" in desc ? !m.__esModule : desc.writable || desc.configurable)) {\n desc = { enumerable: true, get: function() { return m[k]; } };\n }\n Object.defineProperty(o, k2, desc);\n}) : (function(o, m, k, k2) {\n if (k2 === undefined) k2 = k;\n o[k2] = m[k];\n});\n\nexport function __exportStar(m, o) {\n for (var p in m) if (p !== \"default\" && !Object.prototype.hasOwnProperty.call(o, p)) __createBinding(o, m, p);\n}\n\nexport function __values(o) {\n var s = typeof Symbol === \"function\" && Symbol.iterator, m = s && o[s], i = 0;\n if (m) return m.call(o);\n if (o && typeof o.length === \"number\") return {\n next: function () {\n if (o && i >= o.length) o = void 0;\n return { value: o && o[i++], done: !o };\n }\n };\n throw new TypeError(s ? \"Object is not iterable.\" : \"Symbol.iterator is not defined.\");\n}\n\nexport function __read(o, n) {\n var m = typeof Symbol === \"function\" && o[Symbol.iterator];\n if (!m) return o;\n var i = m.call(o), r, ar = [], e;\n try {\n while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);\n }\n catch (error) { e = { error: error }; }\n finally {\n try {\n if (r && !r.done && (m = i[\"return\"])) m.call(i);\n }\n finally { if (e) throw e.error; }\n }\n return ar;\n}\n\n/** @deprecated */\nexport function __spread() {\n for (var ar = [], i = 0; i < arguments.length; i++)\n ar = ar.concat(__read(arguments[i]));\n return ar;\n}\n\n/** @deprecated */\nexport function __spreadArrays() {\n for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;\n for (var r = Array(s), k = 0, i = 0; i < il; i++)\n for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)\n r[k] = a[j];\n return r;\n}\n\nexport function __spreadArray(to, from, pack) {\n if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {\n if (ar || !(i in from)) {\n if (!ar) ar = Array.prototype.slice.call(from, 0, i);\n ar[i] = from[i];\n }\n }\n return to.concat(ar || Array.prototype.slice.call(from));\n}\n\nexport function __await(v) {\n return this instanceof __await ? (this.v = v, this) : new __await(v);\n}\n\nexport function __asyncGenerator(thisArg, _arguments, generator) {\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\n var g = generator.apply(thisArg, _arguments || []), i, q = [];\n return i = Object.create((typeof AsyncIterator === \"function\" ? AsyncIterator : Object).prototype), verb(\"next\"), verb(\"throw\"), verb(\"return\", awaitReturn), i[Symbol.asyncIterator] = function () { return this; }, i;\n function awaitReturn(f) { return function (v) { return Promise.resolve(v).then(f, reject); }; }\n function verb(n, f) { if (g[n]) { i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; if (f) i[n] = f(i[n]); } }\n function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }\n function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }\n function fulfill(value) { resume(\"next\", value); }\n function reject(value) { resume(\"throw\", value); }\n function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }\n}\n\nexport function __asyncDelegator(o) {\n var i, p;\n return i = {}, verb(\"next\"), verb(\"throw\", function (e) { throw e; }), verb(\"return\"), i[Symbol.iterator] = function () { return this; }, i;\n function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: false } : f ? f(v) : v; } : f; }\n}\n\nexport function __asyncValues(o) {\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\n var m = o[Symbol.asyncIterator], i;\n return m ? m.call(o) : (o = typeof __values === \"function\" ? __values(o) : o[Symbol.iterator](), i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i);\n function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }\n function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }\n}\n\nexport function __makeTemplateObject(cooked, raw) {\n if (Object.defineProperty) { Object.defineProperty(cooked, \"raw\", { value: raw }); } else { cooked.raw = raw; }\n return cooked;\n};\n\nvar __setModuleDefault = Object.create ? (function(o, v) {\n Object.defineProperty(o, \"default\", { enumerable: true, value: v });\n}) : function(o, v) {\n o[\"default\"] = v;\n};\n\nexport function __importStar(mod) {\n if (mod && mod.__esModule) return mod;\n var result = {};\n if (mod != null) for (var k in mod) if (k !== \"default\" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);\n __setModuleDefault(result, mod);\n return result;\n}\n\nexport function __importDefault(mod) {\n return (mod && mod.__esModule) ? mod : { default: mod };\n}\n\nexport function __classPrivateFieldGet(receiver, state, kind, f) {\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a getter\");\n if (typeof state === \"function\" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError(\"Cannot read private member from an object whose class did not declare it\");\n return kind === \"m\" ? f : kind === \"a\" ? f.call(receiver) : f ? f.value : state.get(receiver);\n}\n\nexport function __classPrivateFieldSet(receiver, state, value, kind, f) {\n if (kind === \"m\") throw new TypeError(\"Private method is not writable\");\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a setter\");\n if (typeof state === \"function\" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError(\"Cannot write private member to an object whose class did not declare it\");\n return (kind === \"a\" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;\n}\n\nexport function __classPrivateFieldIn(state, receiver) {\n if (receiver === null || (typeof receiver !== \"object\" && typeof receiver !== \"function\")) throw new TypeError(\"Cannot use 'in' operator on non-object\");\n return typeof state === \"function\" ? receiver === state : state.has(receiver);\n}\n\nexport function __addDisposableResource(env, value, async) {\n if (value !== null && value !== void 0) {\n if (typeof value !== \"object\" && typeof value !== \"function\") throw new TypeError(\"Object expected.\");\n var dispose, inner;\n if (async) {\n if (!Symbol.asyncDispose) throw new TypeError(\"Symbol.asyncDispose is not defined.\");\n dispose = value[Symbol.asyncDispose];\n }\n if (dispose === void 0) {\n if (!Symbol.dispose) throw new TypeError(\"Symbol.dispose is not defined.\");\n dispose = value[Symbol.dispose];\n if (async) inner = dispose;\n }\n if (typeof dispose !== \"function\") throw new TypeError(\"Object not disposable.\");\n if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };\n env.stack.push({ value: value, dispose: dispose, async: async });\n }\n else if (async) {\n env.stack.push({ async: true });\n }\n return value;\n}\n\nvar _SuppressedError = typeof SuppressedError === \"function\" ? SuppressedError : function (error, suppressed, message) {\n var e = new Error(message);\n return e.name = \"SuppressedError\", e.error = error, e.suppressed = suppressed, e;\n};\n\nexport function __disposeResources(env) {\n function fail(e) {\n env.error = env.hasError ? new _SuppressedError(e, env.error, \"An error was suppressed during disposal.\") : e;\n env.hasError = true;\n }\n var r, s = 0;\n function next() {\n while (r = env.stack.pop()) {\n try {\n if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);\n if (r.dispose) {\n var result = r.dispose.call(r.value);\n if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });\n }\n else s |= 1;\n }\n catch (e) {\n fail(e);\n }\n }\n if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();\n if (env.hasError) throw env.error;\n }\n return next();\n}\n\nexport default {\n __extends,\n __assign,\n __rest,\n __decorate,\n __param,\n __metadata,\n __awaiter,\n __generator,\n __createBinding,\n __exportStar,\n __values,\n __read,\n __spread,\n __spreadArrays,\n __spreadArray,\n __await,\n __asyncGenerator,\n __asyncDelegator,\n __asyncValues,\n __makeTemplateObject,\n __importStar,\n __importDefault,\n __classPrivateFieldGet,\n __classPrivateFieldSet,\n __classPrivateFieldIn,\n __addDisposableResource,\n __disposeResources,\n};\n", "/**\n * Returns true if the object is a function.\n * @param value The value to check\n */\nexport function isFunction(value: any): value is (...args: any[]) => any {\n return typeof value === 'function';\n}\n", "/**\n * Used to create Error subclasses until the community moves away from ES5.\n *\n * This is because compiling from TypeScript down to ES5 has issues with subclassing Errors\n * as well as other built-in types: https://github.com/Microsoft/TypeScript/issues/12123\n *\n * @param createImpl A factory function to create the actual constructor implementation. The returned\n * function should be a named function that calls `_super` internally.\n */\nexport function createErrorClass(createImpl: (_super: any) => any): T {\n const _super = (instance: any) => {\n Error.call(instance);\n instance.stack = new Error().stack;\n };\n\n const ctorFunc = createImpl(_super);\n ctorFunc.prototype = Object.create(Error.prototype);\n ctorFunc.prototype.constructor = ctorFunc;\n return ctorFunc;\n}\n", "import { createErrorClass } from './createErrorClass';\n\nexport interface UnsubscriptionError extends Error {\n readonly errors: any[];\n}\n\nexport interface UnsubscriptionErrorCtor {\n /**\n * @deprecated Internal implementation detail. Do not construct error instances.\n * Cannot be tagged as internal: https://github.com/ReactiveX/rxjs/issues/6269\n */\n new (errors: any[]): UnsubscriptionError;\n}\n\n/**\n * An error thrown when one or more errors have occurred during the\n * `unsubscribe` of a {@link Subscription}.\n */\nexport const UnsubscriptionError: UnsubscriptionErrorCtor = createErrorClass(\n (_super) =>\n function UnsubscriptionErrorImpl(this: any, errors: (Error | string)[]) {\n _super(this);\n this.message = errors\n ? `${errors.length} errors occurred during unsubscription:\n${errors.map((err, i) => `${i + 1}) ${err.toString()}`).join('\\n ')}`\n : '';\n this.name = 'UnsubscriptionError';\n this.errors = errors;\n }\n);\n", "/**\n * Removes an item from an array, mutating it.\n * @param arr The array to remove the item from\n * @param item The item to remove\n */\nexport function arrRemove(arr: T[] | undefined | null, item: T) {\n if (arr) {\n const index = arr.indexOf(item);\n 0 <= index && arr.splice(index, 1);\n }\n}\n", "import { isFunction } from './util/isFunction';\nimport { UnsubscriptionError } from './util/UnsubscriptionError';\nimport { SubscriptionLike, TeardownLogic, Unsubscribable } from './types';\nimport { arrRemove } from './util/arrRemove';\n\n/**\n * Represents a disposable resource, such as the execution of an Observable. A\n * Subscription has one important method, `unsubscribe`, that takes no argument\n * and just disposes the resource held by the subscription.\n *\n * Additionally, subscriptions may be grouped together through the `add()`\n * method, which will attach a child Subscription to the current Subscription.\n * When a Subscription is unsubscribed, all its children (and its grandchildren)\n * will be unsubscribed as well.\n */\nexport class Subscription implements SubscriptionLike {\n public static EMPTY = (() => {\n const empty = new Subscription();\n empty.closed = true;\n return empty;\n })();\n\n /**\n * A flag to indicate whether this Subscription has already been unsubscribed.\n */\n public closed = false;\n\n private _parentage: Subscription[] | Subscription | null = null;\n\n /**\n * The list of registered finalizers to execute upon unsubscription. Adding and removing from this\n * list occurs in the {@link #add} and {@link #remove} methods.\n */\n private _finalizers: Exclude[] | null = null;\n\n /**\n * @param initialTeardown A function executed first as part of the finalization\n * process that is kicked off when {@link #unsubscribe} is called.\n */\n constructor(private initialTeardown?: () => void) {}\n\n /**\n * Disposes the resources held by the subscription. May, for instance, cancel\n * an ongoing Observable execution or cancel any other type of work that\n * started when the Subscription was created.\n */\n unsubscribe(): void {\n let errors: any[] | undefined;\n\n if (!this.closed) {\n this.closed = true;\n\n // Remove this from it's parents.\n const { _parentage } = this;\n if (_parentage) {\n this._parentage = null;\n if (Array.isArray(_parentage)) {\n for (const parent of _parentage) {\n parent.remove(this);\n }\n } else {\n _parentage.remove(this);\n }\n }\n\n const { initialTeardown: initialFinalizer } = this;\n if (isFunction(initialFinalizer)) {\n try {\n initialFinalizer();\n } catch (e) {\n errors = e instanceof UnsubscriptionError ? e.errors : [e];\n }\n }\n\n const { _finalizers } = this;\n if (_finalizers) {\n this._finalizers = null;\n for (const finalizer of _finalizers) {\n try {\n execFinalizer(finalizer);\n } catch (err) {\n errors = errors ?? [];\n if (err instanceof UnsubscriptionError) {\n errors = [...errors, ...err.errors];\n } else {\n errors.push(err);\n }\n }\n }\n }\n\n if (errors) {\n throw new UnsubscriptionError(errors);\n }\n }\n }\n\n /**\n * Adds a finalizer to this subscription, so that finalization will be unsubscribed/called\n * when this subscription is unsubscribed. If this subscription is already {@link #closed},\n * because it has already been unsubscribed, then whatever finalizer is passed to it\n * will automatically be executed (unless the finalizer itself is also a closed subscription).\n *\n * Closed Subscriptions cannot be added as finalizers to any subscription. Adding a closed\n * subscription to a any subscription will result in no operation. (A noop).\n *\n * Adding a subscription to itself, or adding `null` or `undefined` will not perform any\n * operation at all. (A noop).\n *\n * `Subscription` instances that are added to this instance will automatically remove themselves\n * if they are unsubscribed. Functions and {@link Unsubscribable} objects that you wish to remove\n * will need to be removed manually with {@link #remove}\n *\n * @param teardown The finalization logic to add to this subscription.\n */\n add(teardown: TeardownLogic): void {\n // Only add the finalizer if it's not undefined\n // and don't add a subscription to itself.\n if (teardown && teardown !== this) {\n if (this.closed) {\n // If this subscription is already closed,\n // execute whatever finalizer is handed to it automatically.\n execFinalizer(teardown);\n } else {\n if (teardown instanceof Subscription) {\n // We don't add closed subscriptions, and we don't add the same subscription\n // twice. Subscription unsubscribe is idempotent.\n if (teardown.closed || teardown._hasParent(this)) {\n return;\n }\n teardown._addParent(this);\n }\n (this._finalizers = this._finalizers ?? []).push(teardown);\n }\n }\n }\n\n /**\n * Checks to see if a this subscription already has a particular parent.\n * This will signal that this subscription has already been added to the parent in question.\n * @param parent the parent to check for\n */\n private _hasParent(parent: Subscription) {\n const { _parentage } = this;\n return _parentage === parent || (Array.isArray(_parentage) && _parentage.includes(parent));\n }\n\n /**\n * Adds a parent to this subscription so it can be removed from the parent if it\n * unsubscribes on it's own.\n *\n * NOTE: THIS ASSUMES THAT {@link _hasParent} HAS ALREADY BEEN CHECKED.\n * @param parent The parent subscription to add\n */\n private _addParent(parent: Subscription) {\n const { _parentage } = this;\n this._parentage = Array.isArray(_parentage) ? (_parentage.push(parent), _parentage) : _parentage ? [_parentage, parent] : parent;\n }\n\n /**\n * Called on a child when it is removed via {@link #remove}.\n * @param parent The parent to remove\n */\n private _removeParent(parent: Subscription) {\n const { _parentage } = this;\n if (_parentage === parent) {\n this._parentage = null;\n } else if (Array.isArray(_parentage)) {\n arrRemove(_parentage, parent);\n }\n }\n\n /**\n * Removes a finalizer from this subscription that was previously added with the {@link #add} method.\n *\n * Note that `Subscription` instances, when unsubscribed, will automatically remove themselves\n * from every other `Subscription` they have been added to. This means that using the `remove` method\n * is not a common thing and should be used thoughtfully.\n *\n * If you add the same finalizer instance of a function or an unsubscribable object to a `Subscription` instance\n * more than once, you will need to call `remove` the same number of times to remove all instances.\n *\n * All finalizer instances are removed to free up memory upon unsubscription.\n *\n * @param teardown The finalizer to remove from this subscription\n */\n remove(teardown: Exclude): void {\n const { _finalizers } = this;\n _finalizers && arrRemove(_finalizers, teardown);\n\n if (teardown instanceof Subscription) {\n teardown._removeParent(this);\n }\n }\n}\n\nexport const EMPTY_SUBSCRIPTION = Subscription.EMPTY;\n\nexport function isSubscription(value: any): value is Subscription {\n return (\n value instanceof Subscription ||\n (value && 'closed' in value && isFunction(value.remove) && isFunction(value.add) && isFunction(value.unsubscribe))\n );\n}\n\nfunction execFinalizer(finalizer: Unsubscribable | (() => void)) {\n if (isFunction(finalizer)) {\n finalizer();\n } else {\n finalizer.unsubscribe();\n }\n}\n", "import { Subscriber } from './Subscriber';\nimport { ObservableNotification } from './types';\n\n/**\n * The {@link GlobalConfig} object for RxJS. It is used to configure things\n * like how to react on unhandled errors.\n */\nexport const config: GlobalConfig = {\n onUnhandledError: null,\n onStoppedNotification: null,\n Promise: undefined,\n useDeprecatedSynchronousErrorHandling: false,\n useDeprecatedNextContext: false,\n};\n\n/**\n * The global configuration object for RxJS, used to configure things\n * like how to react on unhandled errors. Accessible via {@link config}\n * object.\n */\nexport interface GlobalConfig {\n /**\n * A registration point for unhandled errors from RxJS. These are errors that\n * cannot were not handled by consuming code in the usual subscription path. For\n * example, if you have this configured, and you subscribe to an observable without\n * providing an error handler, errors from that subscription will end up here. This\n * will _always_ be called asynchronously on another job in the runtime. This is because\n * we do not want errors thrown in this user-configured handler to interfere with the\n * behavior of the library.\n */\n onUnhandledError: ((err: any) => void) | null;\n\n /**\n * A registration point for notifications that cannot be sent to subscribers because they\n * have completed, errored or have been explicitly unsubscribed. By default, next, complete\n * and error notifications sent to stopped subscribers are noops. However, sometimes callers\n * might want a different behavior. For example, with sources that attempt to report errors\n * to stopped subscribers, a caller can configure RxJS to throw an unhandled error instead.\n * This will _always_ be called asynchronously on another job in the runtime. This is because\n * we do not want errors thrown in this user-configured handler to interfere with the\n * behavior of the library.\n */\n onStoppedNotification: ((notification: ObservableNotification, subscriber: Subscriber) => void) | null;\n\n /**\n * The promise constructor used by default for {@link Observable#toPromise toPromise} and {@link Observable#forEach forEach}\n * methods.\n *\n * @deprecated As of version 8, RxJS will no longer support this sort of injection of a\n * Promise constructor. If you need a Promise implementation other than native promises,\n * please polyfill/patch Promise as you see appropriate. Will be removed in v8.\n */\n Promise?: PromiseConstructorLike;\n\n /**\n * If true, turns on synchronous error rethrowing, which is a deprecated behavior\n * in v6 and higher. This behavior enables bad patterns like wrapping a subscribe\n * call in a try/catch block. It also enables producer interference, a nasty bug\n * where a multicast can be broken for all observers by a downstream consumer with\n * an unhandled error. DO NOT USE THIS FLAG UNLESS IT'S NEEDED TO BUY TIME\n * FOR MIGRATION REASONS.\n *\n * @deprecated As of version 8, RxJS will no longer support synchronous throwing\n * of unhandled errors. All errors will be thrown on a separate call stack to prevent bad\n * behaviors described above. Will be removed in v8.\n */\n useDeprecatedSynchronousErrorHandling: boolean;\n\n /**\n * If true, enables an as-of-yet undocumented feature from v5: The ability to access\n * `unsubscribe()` via `this` context in `next` functions created in observers passed\n * to `subscribe`.\n *\n * This is being removed because the performance was severely problematic, and it could also cause\n * issues when types other than POJOs are passed to subscribe as subscribers, as they will likely have\n * their `this` context overwritten.\n *\n * @deprecated As of version 8, RxJS will no longer support altering the\n * context of next functions provided as part of an observer to Subscribe. Instead,\n * you will have access to a subscription or a signal or token that will allow you to do things like\n * unsubscribe and test closed status. Will be removed in v8.\n */\n useDeprecatedNextContext: boolean;\n}\n", "import type { TimerHandle } from './timerHandle';\ntype SetTimeoutFunction = (handler: () => void, timeout?: number, ...args: any[]) => TimerHandle;\ntype ClearTimeoutFunction = (handle: TimerHandle) => void;\n\ninterface TimeoutProvider {\n setTimeout: SetTimeoutFunction;\n clearTimeout: ClearTimeoutFunction;\n delegate:\n | {\n setTimeout: SetTimeoutFunction;\n clearTimeout: ClearTimeoutFunction;\n }\n | undefined;\n}\n\nexport const timeoutProvider: TimeoutProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n setTimeout(handler: () => void, timeout?: number, ...args) {\n const { delegate } = timeoutProvider;\n if (delegate?.setTimeout) {\n return delegate.setTimeout(handler, timeout, ...args);\n }\n return setTimeout(handler, timeout, ...args);\n },\n clearTimeout(handle) {\n const { delegate } = timeoutProvider;\n return (delegate?.clearTimeout || clearTimeout)(handle as any);\n },\n delegate: undefined,\n};\n", "import { config } from '../config';\nimport { timeoutProvider } from '../scheduler/timeoutProvider';\n\n/**\n * Handles an error on another job either with the user-configured {@link onUnhandledError},\n * or by throwing it on that new job so it can be picked up by `window.onerror`, `process.on('error')`, etc.\n *\n * This should be called whenever there is an error that is out-of-band with the subscription\n * or when an error hits a terminal boundary of the subscription and no error handler was provided.\n *\n * @param err the error to report\n */\nexport function reportUnhandledError(err: any) {\n timeoutProvider.setTimeout(() => {\n const { onUnhandledError } = config;\n if (onUnhandledError) {\n // Execute the user-configured error handler.\n onUnhandledError(err);\n } else {\n // Throw so it is picked up by the runtime's uncaught error mechanism.\n throw err;\n }\n });\n}\n", "/* tslint:disable:no-empty */\nexport function noop() { }\n", "import { CompleteNotification, NextNotification, ErrorNotification } from './types';\n\n/**\n * A completion object optimized for memory use and created to be the\n * same \"shape\" as other notifications in v8.\n * @internal\n */\nexport const COMPLETE_NOTIFICATION = (() => createNotification('C', undefined, undefined) as CompleteNotification)();\n\n/**\n * Internal use only. Creates an optimized error notification that is the same \"shape\"\n * as other notifications.\n * @internal\n */\nexport function errorNotification(error: any): ErrorNotification {\n return createNotification('E', undefined, error) as any;\n}\n\n/**\n * Internal use only. Creates an optimized next notification that is the same \"shape\"\n * as other notifications.\n * @internal\n */\nexport function nextNotification(value: T) {\n return createNotification('N', value, undefined) as NextNotification;\n}\n\n/**\n * Ensures that all notifications created internally have the same \"shape\" in v8.\n *\n * TODO: This is only exported to support a crazy legacy test in `groupBy`.\n * @internal\n */\nexport function createNotification(kind: 'N' | 'E' | 'C', value: any, error: any) {\n return {\n kind,\n value,\n error,\n };\n}\n", "import { config } from '../config';\n\nlet context: { errorThrown: boolean; error: any } | null = null;\n\n/**\n * Handles dealing with errors for super-gross mode. Creates a context, in which\n * any synchronously thrown errors will be passed to {@link captureError}. Which\n * will record the error such that it will be rethrown after the call back is complete.\n * TODO: Remove in v8\n * @param cb An immediately executed function.\n */\nexport function errorContext(cb: () => void) {\n if (config.useDeprecatedSynchronousErrorHandling) {\n const isRoot = !context;\n if (isRoot) {\n context = { errorThrown: false, error: null };\n }\n cb();\n if (isRoot) {\n const { errorThrown, error } = context!;\n context = null;\n if (errorThrown) {\n throw error;\n }\n }\n } else {\n // This is the general non-deprecated path for everyone that\n // isn't crazy enough to use super-gross mode (useDeprecatedSynchronousErrorHandling)\n cb();\n }\n}\n\n/**\n * Captures errors only in super-gross mode.\n * @param err the error to capture\n */\nexport function captureError(err: any) {\n if (config.useDeprecatedSynchronousErrorHandling && context) {\n context.errorThrown = true;\n context.error = err;\n }\n}\n", "import { isFunction } from './util/isFunction';\nimport { Observer, ObservableNotification } from './types';\nimport { isSubscription, Subscription } from './Subscription';\nimport { config } from './config';\nimport { reportUnhandledError } from './util/reportUnhandledError';\nimport { noop } from './util/noop';\nimport { nextNotification, errorNotification, COMPLETE_NOTIFICATION } from './NotificationFactories';\nimport { timeoutProvider } from './scheduler/timeoutProvider';\nimport { captureError } from './util/errorContext';\n\n/**\n * Implements the {@link Observer} interface and extends the\n * {@link Subscription} class. While the {@link Observer} is the public API for\n * consuming the values of an {@link Observable}, all Observers get converted to\n * a Subscriber, in order to provide Subscription-like capabilities such as\n * `unsubscribe`. Subscriber is a common type in RxJS, and crucial for\n * implementing operators, but it is rarely used as a public API.\n */\nexport class Subscriber extends Subscription implements Observer {\n /**\n * A static factory for a Subscriber, given a (potentially partial) definition\n * of an Observer.\n * @param next The `next` callback of an Observer.\n * @param error The `error` callback of an\n * Observer.\n * @param complete The `complete` callback of an\n * Observer.\n * @return A Subscriber wrapping the (partially defined)\n * Observer represented by the given arguments.\n * @deprecated Do not use. Will be removed in v8. There is no replacement for this\n * method, and there is no reason to be creating instances of `Subscriber` directly.\n * If you have a specific use case, please file an issue.\n */\n static create(next?: (x?: T) => void, error?: (e?: any) => void, complete?: () => void): Subscriber {\n return new SafeSubscriber(next, error, complete);\n }\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n protected isStopped: boolean = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n protected destination: Subscriber | Observer; // this `any` is the escape hatch to erase extra type param (e.g. R)\n\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n * There is no reason to directly create an instance of Subscriber. This type is exported for typings reasons.\n */\n constructor(destination?: Subscriber | Observer) {\n super();\n if (destination) {\n this.destination = destination;\n // Automatically chain subscriptions together here.\n // if destination is a Subscription, then it is a Subscriber.\n if (isSubscription(destination)) {\n destination.add(this);\n }\n } else {\n this.destination = EMPTY_OBSERVER;\n }\n }\n\n /**\n * The {@link Observer} callback to receive notifications of type `next` from\n * the Observable, with a value. The Observable may call this method 0 or more\n * times.\n * @param value The `next` value.\n */\n next(value: T): void {\n if (this.isStopped) {\n handleStoppedNotification(nextNotification(value), this);\n } else {\n this._next(value!);\n }\n }\n\n /**\n * The {@link Observer} callback to receive notifications of type `error` from\n * the Observable, with an attached `Error`. Notifies the Observer that\n * the Observable has experienced an error condition.\n * @param err The `error` exception.\n */\n error(err?: any): void {\n if (this.isStopped) {\n handleStoppedNotification(errorNotification(err), this);\n } else {\n this.isStopped = true;\n this._error(err);\n }\n }\n\n /**\n * The {@link Observer} callback to receive a valueless notification of type\n * `complete` from the Observable. Notifies the Observer that the Observable\n * has finished sending push-based notifications.\n */\n complete(): void {\n if (this.isStopped) {\n handleStoppedNotification(COMPLETE_NOTIFICATION, this);\n } else {\n this.isStopped = true;\n this._complete();\n }\n }\n\n unsubscribe(): void {\n if (!this.closed) {\n this.isStopped = true;\n super.unsubscribe();\n this.destination = null!;\n }\n }\n\n protected _next(value: T): void {\n this.destination.next(value);\n }\n\n protected _error(err: any): void {\n try {\n this.destination.error(err);\n } finally {\n this.unsubscribe();\n }\n }\n\n protected _complete(): void {\n try {\n this.destination.complete();\n } finally {\n this.unsubscribe();\n }\n }\n}\n\n/**\n * This bind is captured here because we want to be able to have\n * compatibility with monoid libraries that tend to use a method named\n * `bind`. In particular, a library called Monio requires this.\n */\nconst _bind = Function.prototype.bind;\n\nfunction bind any>(fn: Fn, thisArg: any): Fn {\n return _bind.call(fn, thisArg);\n}\n\n/**\n * Internal optimization only, DO NOT EXPOSE.\n * @internal\n */\nclass ConsumerObserver implements Observer {\n constructor(private partialObserver: Partial>) {}\n\n next(value: T): void {\n const { partialObserver } = this;\n if (partialObserver.next) {\n try {\n partialObserver.next(value);\n } catch (error) {\n handleUnhandledError(error);\n }\n }\n }\n\n error(err: any): void {\n const { partialObserver } = this;\n if (partialObserver.error) {\n try {\n partialObserver.error(err);\n } catch (error) {\n handleUnhandledError(error);\n }\n } else {\n handleUnhandledError(err);\n }\n }\n\n complete(): void {\n const { partialObserver } = this;\n if (partialObserver.complete) {\n try {\n partialObserver.complete();\n } catch (error) {\n handleUnhandledError(error);\n }\n }\n }\n}\n\nexport class SafeSubscriber extends Subscriber {\n constructor(\n observerOrNext?: Partial> | ((value: T) => void) | null,\n error?: ((e?: any) => void) | null,\n complete?: (() => void) | null\n ) {\n super();\n\n let partialObserver: Partial>;\n if (isFunction(observerOrNext) || !observerOrNext) {\n // The first argument is a function, not an observer. The next\n // two arguments *could* be observers, or they could be empty.\n partialObserver = {\n next: (observerOrNext ?? undefined) as ((value: T) => void) | undefined,\n error: error ?? undefined,\n complete: complete ?? undefined,\n };\n } else {\n // The first argument is a partial observer.\n let context: any;\n if (this && config.useDeprecatedNextContext) {\n // This is a deprecated path that made `this.unsubscribe()` available in\n // next handler functions passed to subscribe. This only exists behind a flag\n // now, as it is *very* slow.\n context = Object.create(observerOrNext);\n context.unsubscribe = () => this.unsubscribe();\n partialObserver = {\n next: observerOrNext.next && bind(observerOrNext.next, context),\n error: observerOrNext.error && bind(observerOrNext.error, context),\n complete: observerOrNext.complete && bind(observerOrNext.complete, context),\n };\n } else {\n // The \"normal\" path. Just use the partial observer directly.\n partialObserver = observerOrNext;\n }\n }\n\n // Wrap the partial observer to ensure it's a full observer, and\n // make sure proper error handling is accounted for.\n this.destination = new ConsumerObserver(partialObserver);\n }\n}\n\nfunction handleUnhandledError(error: any) {\n if (config.useDeprecatedSynchronousErrorHandling) {\n captureError(error);\n } else {\n // Ideal path, we report this as an unhandled error,\n // which is thrown on a new call stack.\n reportUnhandledError(error);\n }\n}\n\n/**\n * An error handler used when no error handler was supplied\n * to the SafeSubscriber -- meaning no error handler was supplied\n * do the `subscribe` call on our observable.\n * @param err The error to handle\n */\nfunction defaultErrorHandler(err: any) {\n throw err;\n}\n\n/**\n * A handler for notifications that cannot be sent to a stopped subscriber.\n * @param notification The notification being sent.\n * @param subscriber The stopped subscriber.\n */\nfunction handleStoppedNotification(notification: ObservableNotification, subscriber: Subscriber) {\n const { onStoppedNotification } = config;\n onStoppedNotification && timeoutProvider.setTimeout(() => onStoppedNotification(notification, subscriber));\n}\n\n/**\n * The observer used as a stub for subscriptions where the user did not\n * pass any arguments to `subscribe`. Comes with the default error handling\n * behavior.\n */\nexport const EMPTY_OBSERVER: Readonly> & { closed: true } = {\n closed: true,\n next: noop,\n error: defaultErrorHandler,\n complete: noop,\n};\n", "/**\n * Symbol.observable or a string \"@@observable\". Used for interop\n *\n * @deprecated We will no longer be exporting this symbol in upcoming versions of RxJS.\n * Instead polyfill and use Symbol.observable directly *or* use https://www.npmjs.com/package/symbol-observable\n */\nexport const observable: string | symbol = (() => (typeof Symbol === 'function' && Symbol.observable) || '@@observable')();\n", "/**\n * This function takes one parameter and just returns it. Simply put,\n * this is like `(x: T): T => x`.\n *\n * ## Examples\n *\n * This is useful in some cases when using things like `mergeMap`\n *\n * ```ts\n * import { interval, take, map, range, mergeMap, identity } from 'rxjs';\n *\n * const source$ = interval(1000).pipe(take(5));\n *\n * const result$ = source$.pipe(\n * map(i => range(i)),\n * mergeMap(identity) // same as mergeMap(x => x)\n * );\n *\n * result$.subscribe({\n * next: console.log\n * });\n * ```\n *\n * Or when you want to selectively apply an operator\n *\n * ```ts\n * import { interval, take, identity } from 'rxjs';\n *\n * const shouldLimit = () => Math.random() < 0.5;\n *\n * const source$ = interval(1000);\n *\n * const result$ = source$.pipe(shouldLimit() ? take(5) : identity);\n *\n * result$.subscribe({\n * next: console.log\n * });\n * ```\n *\n * @param x Any value that is returned by this function\n * @returns The value passed as the first parameter to this function\n */\nexport function identity(x: T): T {\n return x;\n}\n", "import { identity } from './identity';\nimport { UnaryFunction } from '../types';\n\nexport function pipe(): typeof identity;\nexport function pipe(fn1: UnaryFunction): UnaryFunction;\nexport function pipe(fn1: UnaryFunction, fn2: UnaryFunction): UnaryFunction;\nexport function pipe(fn1: UnaryFunction, fn2: UnaryFunction, fn3: UnaryFunction): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction,\n fn9: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction,\n fn9: UnaryFunction,\n ...fns: UnaryFunction[]\n): UnaryFunction;\n\n/**\n * pipe() can be called on one or more functions, each of which can take one argument (\"UnaryFunction\")\n * and uses it to return a value.\n * It returns a function that takes one argument, passes it to the first UnaryFunction, and then\n * passes the result to the next one, passes that result to the next one, and so on. \n */\nexport function pipe(...fns: Array>): UnaryFunction {\n return pipeFromArray(fns);\n}\n\n/** @internal */\nexport function pipeFromArray(fns: Array>): UnaryFunction {\n if (fns.length === 0) {\n return identity as UnaryFunction;\n }\n\n if (fns.length === 1) {\n return fns[0];\n }\n\n return function piped(input: T): R {\n return fns.reduce((prev: any, fn: UnaryFunction) => fn(prev), input as any);\n };\n}\n", "import { Operator } from './Operator';\nimport { SafeSubscriber, Subscriber } from './Subscriber';\nimport { isSubscription, Subscription } from './Subscription';\nimport { TeardownLogic, OperatorFunction, Subscribable, Observer } from './types';\nimport { observable as Symbol_observable } from './symbol/observable';\nimport { pipeFromArray } from './util/pipe';\nimport { config } from './config';\nimport { isFunction } from './util/isFunction';\nimport { errorContext } from './util/errorContext';\n\n/**\n * A representation of any set of values over any amount of time. This is the most basic building block\n * of RxJS.\n */\nexport class Observable implements Subscribable {\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n */\n source: Observable | undefined;\n\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n */\n operator: Operator | undefined;\n\n /**\n * @param subscribe The function that is called when the Observable is\n * initially subscribed to. This function is given a Subscriber, to which new values\n * can be `next`ed, or an `error` method can be called to raise an error, or\n * `complete` can be called to notify of a successful completion.\n */\n constructor(subscribe?: (this: Observable, subscriber: Subscriber) => TeardownLogic) {\n if (subscribe) {\n this._subscribe = subscribe;\n }\n }\n\n // HACK: Since TypeScript inherits static properties too, we have to\n // fight against TypeScript here so Subject can have a different static create signature\n /**\n * Creates a new Observable by calling the Observable constructor\n * @param subscribe the subscriber function to be passed to the Observable constructor\n * @return A new observable.\n * @deprecated Use `new Observable()` instead. Will be removed in v8.\n */\n static create: (...args: any[]) => any = (subscribe?: (subscriber: Subscriber) => TeardownLogic) => {\n return new Observable(subscribe);\n };\n\n /**\n * Creates a new Observable, with this Observable instance as the source, and the passed\n * operator defined as the new observable's operator.\n * @param operator the operator defining the operation to take on the observable\n * @return A new observable with the Operator applied.\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n * If you have implemented an operator using `lift`, it is recommended that you create an\n * operator by simply returning `new Observable()` directly. See \"Creating new operators from\n * scratch\" section here: https://rxjs.dev/guide/operators\n */\n lift(operator?: Operator): Observable {\n const observable = new Observable();\n observable.source = this;\n observable.operator = operator;\n return observable;\n }\n\n subscribe(observerOrNext?: Partial> | ((value: T) => void)): Subscription;\n /** @deprecated Instead of passing separate callback arguments, use an observer argument. Signatures taking separate callback arguments will be removed in v8. Details: https://rxjs.dev/deprecations/subscribe-arguments */\n subscribe(next?: ((value: T) => void) | null, error?: ((error: any) => void) | null, complete?: (() => void) | null): Subscription;\n /**\n * Invokes an execution of an Observable and registers Observer handlers for notifications it will emit.\n *\n * Use it when you have all these Observables, but still nothing is happening.\n *\n * `subscribe` is not a regular operator, but a method that calls Observable's internal `subscribe` function. It\n * might be for example a function that you passed to Observable's constructor, but most of the time it is\n * a library implementation, which defines what will be emitted by an Observable, and when it be will emitted. This means\n * that calling `subscribe` is actually the moment when Observable starts its work, not when it is created, as it is often\n * the thought.\n *\n * Apart from starting the execution of an Observable, this method allows you to listen for values\n * that an Observable emits, as well as for when it completes or errors. You can achieve this in two\n * of the following ways.\n *\n * The first way is creating an object that implements {@link Observer} interface. It should have methods\n * defined by that interface, but note that it should be just a regular JavaScript object, which you can create\n * yourself in any way you want (ES6 class, classic function constructor, object literal etc.). In particular, do\n * not attempt to use any RxJS implementation details to create Observers - you don't need them. Remember also\n * that your object does not have to implement all methods. If you find yourself creating a method that doesn't\n * do anything, you can simply omit it. Note however, if the `error` method is not provided and an error happens,\n * it will be thrown asynchronously. Errors thrown asynchronously cannot be caught using `try`/`catch`. Instead,\n * use the {@link onUnhandledError} configuration option or use a runtime handler (like `window.onerror` or\n * `process.on('error)`) to be notified of unhandled errors. Because of this, it's recommended that you provide\n * an `error` method to avoid missing thrown errors.\n *\n * The second way is to give up on Observer object altogether and simply provide callback functions in place of its methods.\n * This means you can provide three functions as arguments to `subscribe`, where the first function is equivalent\n * of a `next` method, the second of an `error` method and the third of a `complete` method. Just as in case of an Observer,\n * if you do not need to listen for something, you can omit a function by passing `undefined` or `null`,\n * since `subscribe` recognizes these functions by where they were placed in function call. When it comes\n * to the `error` function, as with an Observer, if not provided, errors emitted by an Observable will be thrown asynchronously.\n *\n * You can, however, subscribe with no parameters at all. This may be the case where you're not interested in terminal events\n * and you also handled emissions internally by using operators (e.g. using `tap`).\n *\n * Whichever style of calling `subscribe` you use, in both cases it returns a Subscription object.\n * This object allows you to call `unsubscribe` on it, which in turn will stop the work that an Observable does and will clean\n * up all resources that an Observable used. Note that cancelling a subscription will not call `complete` callback\n * provided to `subscribe` function, which is reserved for a regular completion signal that comes from an Observable.\n *\n * Remember that callbacks provided to `subscribe` are not guaranteed to be called asynchronously.\n * It is an Observable itself that decides when these functions will be called. For example {@link of}\n * by default emits all its values synchronously. Always check documentation for how given Observable\n * will behave when subscribed and if its default behavior can be modified with a `scheduler`.\n *\n * #### Examples\n *\n * Subscribe with an {@link guide/observer Observer}\n *\n * ```ts\n * import { of } from 'rxjs';\n *\n * const sumObserver = {\n * sum: 0,\n * next(value) {\n * console.log('Adding: ' + value);\n * this.sum = this.sum + value;\n * },\n * error() {\n * // We actually could just remove this method,\n * // since we do not really care about errors right now.\n * },\n * complete() {\n * console.log('Sum equals: ' + this.sum);\n * }\n * };\n *\n * of(1, 2, 3) // Synchronously emits 1, 2, 3 and then completes.\n * .subscribe(sumObserver);\n *\n * // Logs:\n * // 'Adding: 1'\n * // 'Adding: 2'\n * // 'Adding: 3'\n * // 'Sum equals: 6'\n * ```\n *\n * Subscribe with functions ({@link deprecations/subscribe-arguments deprecated})\n *\n * ```ts\n * import { of } from 'rxjs'\n *\n * let sum = 0;\n *\n * of(1, 2, 3).subscribe(\n * value => {\n * console.log('Adding: ' + value);\n * sum = sum + value;\n * },\n * undefined,\n * () => console.log('Sum equals: ' + sum)\n * );\n *\n * // Logs:\n * // 'Adding: 1'\n * // 'Adding: 2'\n * // 'Adding: 3'\n * // 'Sum equals: 6'\n * ```\n *\n * Cancel a subscription\n *\n * ```ts\n * import { interval } from 'rxjs';\n *\n * const subscription = interval(1000).subscribe({\n * next(num) {\n * console.log(num)\n * },\n * complete() {\n * // Will not be called, even when cancelling subscription.\n * console.log('completed!');\n * }\n * });\n *\n * setTimeout(() => {\n * subscription.unsubscribe();\n * console.log('unsubscribed!');\n * }, 2500);\n *\n * // Logs:\n * // 0 after 1s\n * // 1 after 2s\n * // 'unsubscribed!' after 2.5s\n * ```\n *\n * @param observerOrNext Either an {@link Observer} with some or all callback methods,\n * or the `next` handler that is called for each value emitted from the subscribed Observable.\n * @param error A handler for a terminal event resulting from an error. If no error handler is provided,\n * the error will be thrown asynchronously as unhandled.\n * @param complete A handler for a terminal event resulting from successful completion.\n * @return A subscription reference to the registered handlers.\n */\n subscribe(\n observerOrNext?: Partial> | ((value: T) => void) | null,\n error?: ((error: any) => void) | null,\n complete?: (() => void) | null\n ): Subscription {\n const subscriber = isSubscriber(observerOrNext) ? observerOrNext : new SafeSubscriber(observerOrNext, error, complete);\n\n errorContext(() => {\n const { operator, source } = this;\n subscriber.add(\n operator\n ? // We're dealing with a subscription in the\n // operator chain to one of our lifted operators.\n operator.call(subscriber, source)\n : source\n ? // If `source` has a value, but `operator` does not, something that\n // had intimate knowledge of our API, like our `Subject`, must have\n // set it. We're going to just call `_subscribe` directly.\n this._subscribe(subscriber)\n : // In all other cases, we're likely wrapping a user-provided initializer\n // function, so we need to catch errors and handle them appropriately.\n this._trySubscribe(subscriber)\n );\n });\n\n return subscriber;\n }\n\n /** @internal */\n protected _trySubscribe(sink: Subscriber): TeardownLogic {\n try {\n return this._subscribe(sink);\n } catch (err) {\n // We don't need to return anything in this case,\n // because it's just going to try to `add()` to a subscription\n // above.\n sink.error(err);\n }\n }\n\n /**\n * Used as a NON-CANCELLABLE means of subscribing to an observable, for use with\n * APIs that expect promises, like `async/await`. You cannot unsubscribe from this.\n *\n * **WARNING**: Only use this with observables you *know* will complete. If the source\n * observable does not complete, you will end up with a promise that is hung up, and\n * potentially all of the state of an async function hanging out in memory. To avoid\n * this situation, look into adding something like {@link timeout}, {@link take},\n * {@link takeWhile}, or {@link takeUntil} amongst others.\n *\n * #### Example\n *\n * ```ts\n * import { interval, take } from 'rxjs';\n *\n * const source$ = interval(1000).pipe(take(4));\n *\n * async function getTotal() {\n * let total = 0;\n *\n * await source$.forEach(value => {\n * total += value;\n * console.log('observable -> ' + value);\n * });\n *\n * return total;\n * }\n *\n * getTotal().then(\n * total => console.log('Total: ' + total)\n * );\n *\n * // Expected:\n * // 'observable -> 0'\n * // 'observable -> 1'\n * // 'observable -> 2'\n * // 'observable -> 3'\n * // 'Total: 6'\n * ```\n *\n * @param next A handler for each value emitted by the observable.\n * @return A promise that either resolves on observable completion or\n * rejects with the handled error.\n */\n forEach(next: (value: T) => void): Promise;\n\n /**\n * @param next a handler for each value emitted by the observable\n * @param promiseCtor a constructor function used to instantiate the Promise\n * @return a promise that either resolves on observable completion or\n * rejects with the handled error\n * @deprecated Passing a Promise constructor will no longer be available\n * in upcoming versions of RxJS. This is because it adds weight to the library, for very\n * little benefit. If you need this functionality, it is recommended that you either\n * polyfill Promise, or you create an adapter to convert the returned native promise\n * to whatever promise implementation you wanted. Will be removed in v8.\n */\n forEach(next: (value: T) => void, promiseCtor: PromiseConstructorLike): Promise;\n\n forEach(next: (value: T) => void, promiseCtor?: PromiseConstructorLike): Promise {\n promiseCtor = getPromiseCtor(promiseCtor);\n\n return new promiseCtor((resolve, reject) => {\n const subscriber = new SafeSubscriber({\n next: (value) => {\n try {\n next(value);\n } catch (err) {\n reject(err);\n subscriber.unsubscribe();\n }\n },\n error: reject,\n complete: resolve,\n });\n this.subscribe(subscriber);\n }) as Promise;\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): TeardownLogic {\n return this.source?.subscribe(subscriber);\n }\n\n /**\n * An interop point defined by the es7-observable spec https://github.com/zenparsing/es-observable\n * @return This instance of the observable.\n */\n [Symbol_observable]() {\n return this;\n }\n\n /* tslint:disable:max-line-length */\n pipe(): Observable;\n pipe(op1: OperatorFunction): Observable;\n pipe(op1: OperatorFunction, op2: OperatorFunction): Observable;\n pipe(op1: OperatorFunction, op2: OperatorFunction, op3: OperatorFunction): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction,\n op9: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction,\n op9: OperatorFunction,\n ...operations: OperatorFunction[]\n ): Observable;\n /* tslint:enable:max-line-length */\n\n /**\n * Used to stitch together functional operators into a chain.\n *\n * ## Example\n *\n * ```ts\n * import { interval, filter, map, scan } from 'rxjs';\n *\n * interval(1000)\n * .pipe(\n * filter(x => x % 2 === 0),\n * map(x => x + x),\n * scan((acc, x) => acc + x)\n * )\n * .subscribe(x => console.log(x));\n * ```\n *\n * @return The Observable result of all the operators having been called\n * in the order they were passed in.\n */\n pipe(...operations: OperatorFunction[]): Observable {\n return pipeFromArray(operations)(this);\n }\n\n /* tslint:disable:max-line-length */\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(): Promise;\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(PromiseCtor: typeof Promise): Promise;\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(PromiseCtor: PromiseConstructorLike): Promise;\n /* tslint:enable:max-line-length */\n\n /**\n * Subscribe to this Observable and get a Promise resolving on\n * `complete` with the last emission (if any).\n *\n * **WARNING**: Only use this with observables you *know* will complete. If the source\n * observable does not complete, you will end up with a promise that is hung up, and\n * potentially all of the state of an async function hanging out in memory. To avoid\n * this situation, look into adding something like {@link timeout}, {@link take},\n * {@link takeWhile}, or {@link takeUntil} amongst others.\n *\n * @param [promiseCtor] a constructor function used to instantiate\n * the Promise\n * @return A Promise that resolves with the last value emit, or\n * rejects on an error. If there were no emissions, Promise\n * resolves with undefined.\n * @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise\n */\n toPromise(promiseCtor?: PromiseConstructorLike): Promise {\n promiseCtor = getPromiseCtor(promiseCtor);\n\n return new promiseCtor((resolve, reject) => {\n let value: T | undefined;\n this.subscribe(\n (x: T) => (value = x),\n (err: any) => reject(err),\n () => resolve(value)\n );\n }) as Promise;\n }\n}\n\n/**\n * Decides between a passed promise constructor from consuming code,\n * A default configured promise constructor, and the native promise\n * constructor and returns it. If nothing can be found, it will throw\n * an error.\n * @param promiseCtor The optional promise constructor to passed by consuming code\n */\nfunction getPromiseCtor(promiseCtor: PromiseConstructorLike | undefined) {\n return promiseCtor ?? config.Promise ?? Promise;\n}\n\nfunction isObserver(value: any): value is Observer {\n return value && isFunction(value.next) && isFunction(value.error) && isFunction(value.complete);\n}\n\nfunction isSubscriber(value: any): value is Subscriber {\n return (value && value instanceof Subscriber) || (isObserver(value) && isSubscription(value));\n}\n", "import { Observable } from '../Observable';\nimport { Subscriber } from '../Subscriber';\nimport { OperatorFunction } from '../types';\nimport { isFunction } from './isFunction';\n\n/**\n * Used to determine if an object is an Observable with a lift function.\n */\nexport function hasLift(source: any): source is { lift: InstanceType['lift'] } {\n return isFunction(source?.lift);\n}\n\n/**\n * Creates an `OperatorFunction`. Used to define operators throughout the library in a concise way.\n * @param init The logic to connect the liftedSource to the subscriber at the moment of subscription.\n */\nexport function operate(\n init: (liftedSource: Observable, subscriber: Subscriber) => (() => void) | void\n): OperatorFunction {\n return (source: Observable) => {\n if (hasLift(source)) {\n return source.lift(function (this: Subscriber, liftedSource: Observable) {\n try {\n return init(liftedSource, this);\n } catch (err) {\n this.error(err);\n }\n });\n }\n throw new TypeError('Unable to lift unknown Observable type');\n };\n}\n", "import { Subscriber } from '../Subscriber';\n\n/**\n * Creates an instance of an `OperatorSubscriber`.\n * @param destination The downstream subscriber.\n * @param onNext Handles next values, only called if this subscriber is not stopped or closed. Any\n * error that occurs in this function is caught and sent to the `error` method of this subscriber.\n * @param onError Handles errors from the subscription, any errors that occur in this handler are caught\n * and send to the `destination` error handler.\n * @param onComplete Handles completion notification from the subscription. Any errors that occur in\n * this handler are sent to the `destination` error handler.\n * @param onFinalize Additional teardown logic here. This will only be called on teardown if the\n * subscriber itself is not already closed. This is called after all other teardown logic is executed.\n */\nexport function createOperatorSubscriber(\n destination: Subscriber,\n onNext?: (value: T) => void,\n onComplete?: () => void,\n onError?: (err: any) => void,\n onFinalize?: () => void\n): Subscriber {\n return new OperatorSubscriber(destination, onNext, onComplete, onError, onFinalize);\n}\n\n/**\n * A generic helper for allowing operators to be created with a Subscriber and\n * use closures to capture necessary state from the operator function itself.\n */\nexport class OperatorSubscriber extends Subscriber {\n /**\n * Creates an instance of an `OperatorSubscriber`.\n * @param destination The downstream subscriber.\n * @param onNext Handles next values, only called if this subscriber is not stopped or closed. Any\n * error that occurs in this function is caught and sent to the `error` method of this subscriber.\n * @param onError Handles errors from the subscription, any errors that occur in this handler are caught\n * and send to the `destination` error handler.\n * @param onComplete Handles completion notification from the subscription. Any errors that occur in\n * this handler are sent to the `destination` error handler.\n * @param onFinalize Additional finalization logic here. This will only be called on finalization if the\n * subscriber itself is not already closed. This is called after all other finalization logic is executed.\n * @param shouldUnsubscribe An optional check to see if an unsubscribe call should truly unsubscribe.\n * NOTE: This currently **ONLY** exists to support the strange behavior of {@link groupBy}, where unsubscription\n * to the resulting observable does not actually disconnect from the source if there are active subscriptions\n * to any grouped observable. (DO NOT EXPOSE OR USE EXTERNALLY!!!)\n */\n constructor(\n destination: Subscriber,\n onNext?: (value: T) => void,\n onComplete?: () => void,\n onError?: (err: any) => void,\n private onFinalize?: () => void,\n private shouldUnsubscribe?: () => boolean\n ) {\n // It's important - for performance reasons - that all of this class's\n // members are initialized and that they are always initialized in the same\n // order. This will ensure that all OperatorSubscriber instances have the\n // same hidden class in V8. This, in turn, will help keep the number of\n // hidden classes involved in property accesses within the base class as\n // low as possible. If the number of hidden classes involved exceeds four,\n // the property accesses will become megamorphic and performance penalties\n // will be incurred - i.e. inline caches won't be used.\n //\n // The reasons for ensuring all instances have the same hidden class are\n // further discussed in this blog post from Benedikt Meurer:\n // https://benediktmeurer.de/2018/03/23/impact-of-polymorphism-on-component-based-frameworks-like-react/\n super(destination);\n this._next = onNext\n ? function (this: OperatorSubscriber, value: T) {\n try {\n onNext(value);\n } catch (err) {\n destination.error(err);\n }\n }\n : super._next;\n this._error = onError\n ? function (this: OperatorSubscriber, err: any) {\n try {\n onError(err);\n } catch (err) {\n // Send any errors that occur down stream.\n destination.error(err);\n } finally {\n // Ensure finalization.\n this.unsubscribe();\n }\n }\n : super._error;\n this._complete = onComplete\n ? function (this: OperatorSubscriber) {\n try {\n onComplete();\n } catch (err) {\n // Send any errors that occur down stream.\n destination.error(err);\n } finally {\n // Ensure finalization.\n this.unsubscribe();\n }\n }\n : super._complete;\n }\n\n unsubscribe() {\n if (!this.shouldUnsubscribe || this.shouldUnsubscribe()) {\n const { closed } = this;\n super.unsubscribe();\n // Execute additional teardown if we have any and we didn't already do so.\n !closed && this.onFinalize?.();\n }\n }\n}\n", "import { Subscription } from '../Subscription';\n\ninterface AnimationFrameProvider {\n schedule(callback: FrameRequestCallback): Subscription;\n requestAnimationFrame: typeof requestAnimationFrame;\n cancelAnimationFrame: typeof cancelAnimationFrame;\n delegate:\n | {\n requestAnimationFrame: typeof requestAnimationFrame;\n cancelAnimationFrame: typeof cancelAnimationFrame;\n }\n | undefined;\n}\n\nexport const animationFrameProvider: AnimationFrameProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n schedule(callback) {\n let request = requestAnimationFrame;\n let cancel: typeof cancelAnimationFrame | undefined = cancelAnimationFrame;\n const { delegate } = animationFrameProvider;\n if (delegate) {\n request = delegate.requestAnimationFrame;\n cancel = delegate.cancelAnimationFrame;\n }\n const handle = request((timestamp) => {\n // Clear the cancel function. The request has been fulfilled, so\n // attempting to cancel the request upon unsubscription would be\n // pointless.\n cancel = undefined;\n callback(timestamp);\n });\n return new Subscription(() => cancel?.(handle));\n },\n requestAnimationFrame(...args) {\n const { delegate } = animationFrameProvider;\n return (delegate?.requestAnimationFrame || requestAnimationFrame)(...args);\n },\n cancelAnimationFrame(...args) {\n const { delegate } = animationFrameProvider;\n return (delegate?.cancelAnimationFrame || cancelAnimationFrame)(...args);\n },\n delegate: undefined,\n};\n", "import { createErrorClass } from './createErrorClass';\n\nexport interface ObjectUnsubscribedError extends Error {}\n\nexport interface ObjectUnsubscribedErrorCtor {\n /**\n * @deprecated Internal implementation detail. Do not construct error instances.\n * Cannot be tagged as internal: https://github.com/ReactiveX/rxjs/issues/6269\n */\n new (): ObjectUnsubscribedError;\n}\n\n/**\n * An error thrown when an action is invalid because the object has been\n * unsubscribed.\n *\n * @see {@link Subject}\n * @see {@link BehaviorSubject}\n *\n * @class ObjectUnsubscribedError\n */\nexport const ObjectUnsubscribedError: ObjectUnsubscribedErrorCtor = createErrorClass(\n (_super) =>\n function ObjectUnsubscribedErrorImpl(this: any) {\n _super(this);\n this.name = 'ObjectUnsubscribedError';\n this.message = 'object unsubscribed';\n }\n);\n", "import { Operator } from './Operator';\nimport { Observable } from './Observable';\nimport { Subscriber } from './Subscriber';\nimport { Subscription, EMPTY_SUBSCRIPTION } from './Subscription';\nimport { Observer, SubscriptionLike, TeardownLogic } from './types';\nimport { ObjectUnsubscribedError } from './util/ObjectUnsubscribedError';\nimport { arrRemove } from './util/arrRemove';\nimport { errorContext } from './util/errorContext';\n\n/**\n * A Subject is a special type of Observable that allows values to be\n * multicasted to many Observers. Subjects are like EventEmitters.\n *\n * Every Subject is an Observable and an Observer. You can subscribe to a\n * Subject, and you can call next to feed values as well as error and complete.\n */\nexport class Subject extends Observable implements SubscriptionLike {\n closed = false;\n\n private currentObservers: Observer[] | null = null;\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n observers: Observer[] = [];\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n isStopped = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n hasError = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n thrownError: any = null;\n\n /**\n * Creates a \"subject\" by basically gluing an observer to an observable.\n *\n * @deprecated Recommended you do not use. Will be removed at some point in the future. Plans for replacement still under discussion.\n */\n static create: (...args: any[]) => any = (destination: Observer, source: Observable): AnonymousSubject => {\n return new AnonymousSubject(destination, source);\n };\n\n constructor() {\n // NOTE: This must be here to obscure Observable's constructor.\n super();\n }\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n lift(operator: Operator): Observable {\n const subject = new AnonymousSubject(this, this);\n subject.operator = operator as any;\n return subject as any;\n }\n\n /** @internal */\n protected _throwIfClosed() {\n if (this.closed) {\n throw new ObjectUnsubscribedError();\n }\n }\n\n next(value: T) {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n if (!this.currentObservers) {\n this.currentObservers = Array.from(this.observers);\n }\n for (const observer of this.currentObservers) {\n observer.next(value);\n }\n }\n });\n }\n\n error(err: any) {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n this.hasError = this.isStopped = true;\n this.thrownError = err;\n const { observers } = this;\n while (observers.length) {\n observers.shift()!.error(err);\n }\n }\n });\n }\n\n complete() {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n this.isStopped = true;\n const { observers } = this;\n while (observers.length) {\n observers.shift()!.complete();\n }\n }\n });\n }\n\n unsubscribe() {\n this.isStopped = this.closed = true;\n this.observers = this.currentObservers = null!;\n }\n\n get observed() {\n return this.observers?.length > 0;\n }\n\n /** @internal */\n protected _trySubscribe(subscriber: Subscriber): TeardownLogic {\n this._throwIfClosed();\n return super._trySubscribe(subscriber);\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n this._throwIfClosed();\n this._checkFinalizedStatuses(subscriber);\n return this._innerSubscribe(subscriber);\n }\n\n /** @internal */\n protected _innerSubscribe(subscriber: Subscriber) {\n const { hasError, isStopped, observers } = this;\n if (hasError || isStopped) {\n return EMPTY_SUBSCRIPTION;\n }\n this.currentObservers = null;\n observers.push(subscriber);\n return new Subscription(() => {\n this.currentObservers = null;\n arrRemove(observers, subscriber);\n });\n }\n\n /** @internal */\n protected _checkFinalizedStatuses(subscriber: Subscriber) {\n const { hasError, thrownError, isStopped } = this;\n if (hasError) {\n subscriber.error(thrownError);\n } else if (isStopped) {\n subscriber.complete();\n }\n }\n\n /**\n * Creates a new Observable with this Subject as the source. You can do this\n * to create custom Observer-side logic of the Subject and conceal it from\n * code that uses the Observable.\n * @return Observable that this Subject casts to.\n */\n asObservable(): Observable {\n const observable: any = new Observable();\n observable.source = this;\n return observable;\n }\n}\n\nexport class AnonymousSubject extends Subject {\n constructor(\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n public destination?: Observer,\n source?: Observable\n ) {\n super();\n this.source = source;\n }\n\n next(value: T) {\n this.destination?.next?.(value);\n }\n\n error(err: any) {\n this.destination?.error?.(err);\n }\n\n complete() {\n this.destination?.complete?.();\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n return this.source?.subscribe(subscriber) ?? EMPTY_SUBSCRIPTION;\n }\n}\n", "import { Subject } from './Subject';\nimport { Subscriber } from './Subscriber';\nimport { Subscription } from './Subscription';\n\n/**\n * A variant of Subject that requires an initial value and emits its current\n * value whenever it is subscribed to.\n */\nexport class BehaviorSubject extends Subject {\n constructor(private _value: T) {\n super();\n }\n\n get value(): T {\n return this.getValue();\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n const subscription = super._subscribe(subscriber);\n !subscription.closed && subscriber.next(this._value);\n return subscription;\n }\n\n getValue(): T {\n const { hasError, thrownError, _value } = this;\n if (hasError) {\n throw thrownError;\n }\n this._throwIfClosed();\n return _value;\n }\n\n next(value: T): void {\n super.next((this._value = value));\n }\n}\n", "import { TimestampProvider } from '../types';\n\ninterface DateTimestampProvider extends TimestampProvider {\n delegate: TimestampProvider | undefined;\n}\n\nexport const dateTimestampProvider: DateTimestampProvider = {\n now() {\n // Use the variable rather than `this` so that the function can be called\n // without being bound to the provider.\n return (dateTimestampProvider.delegate || Date).now();\n },\n delegate: undefined,\n};\n", "import { Subject } from './Subject';\nimport { TimestampProvider } from './types';\nimport { Subscriber } from './Subscriber';\nimport { Subscription } from './Subscription';\nimport { dateTimestampProvider } from './scheduler/dateTimestampProvider';\n\n/**\n * A variant of {@link Subject} that \"replays\" old values to new subscribers by emitting them when they first subscribe.\n *\n * `ReplaySubject` has an internal buffer that will store a specified number of values that it has observed. Like `Subject`,\n * `ReplaySubject` \"observes\" values by having them passed to its `next` method. When it observes a value, it will store that\n * value for a time determined by the configuration of the `ReplaySubject`, as passed to its constructor.\n *\n * When a new subscriber subscribes to the `ReplaySubject` instance, it will synchronously emit all values in its buffer in\n * a First-In-First-Out (FIFO) manner. The `ReplaySubject` will also complete, if it has observed completion; and it will\n * error if it has observed an error.\n *\n * There are two main configuration items to be concerned with:\n *\n * 1. `bufferSize` - This will determine how many items are stored in the buffer, defaults to infinite.\n * 2. `windowTime` - The amount of time to hold a value in the buffer before removing it from the buffer.\n *\n * Both configurations may exist simultaneously. So if you would like to buffer a maximum of 3 values, as long as the values\n * are less than 2 seconds old, you could do so with a `new ReplaySubject(3, 2000)`.\n *\n * ### Differences with BehaviorSubject\n *\n * `BehaviorSubject` is similar to `new ReplaySubject(1)`, with a couple of exceptions:\n *\n * 1. `BehaviorSubject` comes \"primed\" with a single value upon construction.\n * 2. `ReplaySubject` will replay values, even after observing an error, where `BehaviorSubject` will not.\n *\n * @see {@link Subject}\n * @see {@link BehaviorSubject}\n * @see {@link shareReplay}\n */\nexport class ReplaySubject extends Subject {\n private _buffer: (T | number)[] = [];\n private _infiniteTimeWindow = true;\n\n /**\n * @param _bufferSize The size of the buffer to replay on subscription\n * @param _windowTime The amount of time the buffered items will stay buffered\n * @param _timestampProvider An object with a `now()` method that provides the current timestamp. This is used to\n * calculate the amount of time something has been buffered.\n */\n constructor(\n private _bufferSize = Infinity,\n private _windowTime = Infinity,\n private _timestampProvider: TimestampProvider = dateTimestampProvider\n ) {\n super();\n this._infiniteTimeWindow = _windowTime === Infinity;\n this._bufferSize = Math.max(1, _bufferSize);\n this._windowTime = Math.max(1, _windowTime);\n }\n\n next(value: T): void {\n const { isStopped, _buffer, _infiniteTimeWindow, _timestampProvider, _windowTime } = this;\n if (!isStopped) {\n _buffer.push(value);\n !_infiniteTimeWindow && _buffer.push(_timestampProvider.now() + _windowTime);\n }\n this._trimBuffer();\n super.next(value);\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n this._throwIfClosed();\n this._trimBuffer();\n\n const subscription = this._innerSubscribe(subscriber);\n\n const { _infiniteTimeWindow, _buffer } = this;\n // We use a copy here, so reentrant code does not mutate our array while we're\n // emitting it to a new subscriber.\n const copy = _buffer.slice();\n for (let i = 0; i < copy.length && !subscriber.closed; i += _infiniteTimeWindow ? 1 : 2) {\n subscriber.next(copy[i] as T);\n }\n\n this._checkFinalizedStatuses(subscriber);\n\n return subscription;\n }\n\n private _trimBuffer() {\n const { _bufferSize, _timestampProvider, _buffer, _infiniteTimeWindow } = this;\n // If we don't have an infinite buffer size, and we're over the length,\n // use splice to truncate the old buffer values off. Note that we have to\n // double the size for instances where we're not using an infinite time window\n // because we're storing the values and the timestamps in the same array.\n const adjustedBufferSize = (_infiniteTimeWindow ? 1 : 2) * _bufferSize;\n _bufferSize < Infinity && adjustedBufferSize < _buffer.length && _buffer.splice(0, _buffer.length - adjustedBufferSize);\n\n // Now, if we're not in an infinite time window, remove all values where the time is\n // older than what is allowed.\n if (!_infiniteTimeWindow) {\n const now = _timestampProvider.now();\n let last = 0;\n // Search the array for the first timestamp that isn't expired and\n // truncate the buffer up to that point.\n for (let i = 1; i < _buffer.length && (_buffer[i] as number) <= now; i += 2) {\n last = i;\n }\n last && _buffer.splice(0, last + 1);\n }\n }\n}\n", "import { Scheduler } from '../Scheduler';\nimport { Subscription } from '../Subscription';\nimport { SchedulerAction } from '../types';\n\n/**\n * A unit of work to be executed in a `scheduler`. An action is typically\n * created from within a {@link SchedulerLike} and an RxJS user does not need to concern\n * themselves about creating and manipulating an Action.\n *\n * ```ts\n * class Action extends Subscription {\n * new (scheduler: Scheduler, work: (state?: T) => void);\n * schedule(state?: T, delay: number = 0): Subscription;\n * }\n * ```\n */\nexport class Action extends Subscription {\n constructor(scheduler: Scheduler, work: (this: SchedulerAction, state?: T) => void) {\n super();\n }\n /**\n * Schedules this action on its parent {@link SchedulerLike} for execution. May be passed\n * some context object, `state`. May happen at some point in the future,\n * according to the `delay` parameter, if specified.\n * @param state Some contextual data that the `work` function uses when called by the\n * Scheduler.\n * @param delay Time to wait before executing the work, where the time unit is implicit\n * and defined by the Scheduler.\n * @return A subscription in order to be able to unsubscribe the scheduled work.\n */\n public schedule(state?: T, delay: number = 0): Subscription {\n return this;\n }\n}\n", "import type { TimerHandle } from './timerHandle';\ntype SetIntervalFunction = (handler: () => void, timeout?: number, ...args: any[]) => TimerHandle;\ntype ClearIntervalFunction = (handle: TimerHandle) => void;\n\ninterface IntervalProvider {\n setInterval: SetIntervalFunction;\n clearInterval: ClearIntervalFunction;\n delegate:\n | {\n setInterval: SetIntervalFunction;\n clearInterval: ClearIntervalFunction;\n }\n | undefined;\n}\n\nexport const intervalProvider: IntervalProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n setInterval(handler: () => void, timeout?: number, ...args) {\n const { delegate } = intervalProvider;\n if (delegate?.setInterval) {\n return delegate.setInterval(handler, timeout, ...args);\n }\n return setInterval(handler, timeout, ...args);\n },\n clearInterval(handle) {\n const { delegate } = intervalProvider;\n return (delegate?.clearInterval || clearInterval)(handle as any);\n },\n delegate: undefined,\n};\n", "import { Action } from './Action';\nimport { SchedulerAction } from '../types';\nimport { Subscription } from '../Subscription';\nimport { AsyncScheduler } from './AsyncScheduler';\nimport { intervalProvider } from './intervalProvider';\nimport { arrRemove } from '../util/arrRemove';\nimport { TimerHandle } from './timerHandle';\n\nexport class AsyncAction extends Action {\n public id: TimerHandle | undefined;\n public state?: T;\n // @ts-ignore: Property has no initializer and is not definitely assigned\n public delay: number;\n protected pending: boolean = false;\n\n constructor(protected scheduler: AsyncScheduler, protected work: (this: SchedulerAction, state?: T) => void) {\n super(scheduler, work);\n }\n\n public schedule(state?: T, delay: number = 0): Subscription {\n if (this.closed) {\n return this;\n }\n\n // Always replace the current state with the new state.\n this.state = state;\n\n const id = this.id;\n const scheduler = this.scheduler;\n\n //\n // Important implementation note:\n //\n // Actions only execute once by default, unless rescheduled from within the\n // scheduled callback. This allows us to implement single and repeat\n // actions via the same code path, without adding API surface area, as well\n // as mimic traditional recursion but across asynchronous boundaries.\n //\n // However, JS runtimes and timers distinguish between intervals achieved by\n // serial `setTimeout` calls vs. a single `setInterval` call. An interval of\n // serial `setTimeout` calls can be individually delayed, which delays\n // scheduling the next `setTimeout`, and so on. `setInterval` attempts to\n // guarantee the interval callback will be invoked more precisely to the\n // interval period, regardless of load.\n //\n // Therefore, we use `setInterval` to schedule single and repeat actions.\n // If the action reschedules itself with the same delay, the interval is not\n // canceled. If the action doesn't reschedule, or reschedules with a\n // different delay, the interval will be canceled after scheduled callback\n // execution.\n //\n if (id != null) {\n this.id = this.recycleAsyncId(scheduler, id, delay);\n }\n\n // Set the pending flag indicating that this action has been scheduled, or\n // has recursively rescheduled itself.\n this.pending = true;\n\n this.delay = delay;\n // If this action has already an async Id, don't request a new one.\n this.id = this.id ?? this.requestAsyncId(scheduler, this.id, delay);\n\n return this;\n }\n\n protected requestAsyncId(scheduler: AsyncScheduler, _id?: TimerHandle, delay: number = 0): TimerHandle {\n return intervalProvider.setInterval(scheduler.flush.bind(scheduler, this), delay);\n }\n\n protected recycleAsyncId(_scheduler: AsyncScheduler, id?: TimerHandle, delay: number | null = 0): TimerHandle | undefined {\n // If this action is rescheduled with the same delay time, don't clear the interval id.\n if (delay != null && this.delay === delay && this.pending === false) {\n return id;\n }\n // Otherwise, if the action's delay time is different from the current delay,\n // or the action has been rescheduled before it's executed, clear the interval id\n if (id != null) {\n intervalProvider.clearInterval(id);\n }\n\n return undefined;\n }\n\n /**\n * Immediately executes this action and the `work` it contains.\n */\n public execute(state: T, delay: number): any {\n if (this.closed) {\n return new Error('executing a cancelled action');\n }\n\n this.pending = false;\n const error = this._execute(state, delay);\n if (error) {\n return error;\n } else if (this.pending === false && this.id != null) {\n // Dequeue if the action didn't reschedule itself. Don't call\n // unsubscribe(), because the action could reschedule later.\n // For example:\n // ```\n // scheduler.schedule(function doWork(counter) {\n // /* ... I'm a busy worker bee ... */\n // var originalAction = this;\n // /* wait 100ms before rescheduling the action */\n // setTimeout(function () {\n // originalAction.schedule(counter + 1);\n // }, 100);\n // }, 1000);\n // ```\n this.id = this.recycleAsyncId(this.scheduler, this.id, null);\n }\n }\n\n protected _execute(state: T, _delay: number): any {\n let errored: boolean = false;\n let errorValue: any;\n try {\n this.work(state);\n } catch (e) {\n errored = true;\n // HACK: Since code elsewhere is relying on the \"truthiness\" of the\n // return here, we can't have it return \"\" or 0 or false.\n // TODO: Clean this up when we refactor schedulers mid-version-8 or so.\n errorValue = e ? e : new Error('Scheduled action threw falsy error');\n }\n if (errored) {\n this.unsubscribe();\n return errorValue;\n }\n }\n\n unsubscribe() {\n if (!this.closed) {\n const { id, scheduler } = this;\n const { actions } = scheduler;\n\n this.work = this.state = this.scheduler = null!;\n this.pending = false;\n\n arrRemove(actions, this);\n if (id != null) {\n this.id = this.recycleAsyncId(scheduler, id, null);\n }\n\n this.delay = null!;\n super.unsubscribe();\n }\n }\n}\n", "import { Action } from './scheduler/Action';\nimport { Subscription } from './Subscription';\nimport { SchedulerLike, SchedulerAction } from './types';\nimport { dateTimestampProvider } from './scheduler/dateTimestampProvider';\n\n/**\n * An execution context and a data structure to order tasks and schedule their\n * execution. Provides a notion of (potentially virtual) time, through the\n * `now()` getter method.\n *\n * Each unit of work in a Scheduler is called an `Action`.\n *\n * ```ts\n * class Scheduler {\n * now(): number;\n * schedule(work, delay?, state?): Subscription;\n * }\n * ```\n *\n * @deprecated Scheduler is an internal implementation detail of RxJS, and\n * should not be used directly. Rather, create your own class and implement\n * {@link SchedulerLike}. Will be made internal in v8.\n */\nexport class Scheduler implements SchedulerLike {\n public static now: () => number = dateTimestampProvider.now;\n\n constructor(private schedulerActionCtor: typeof Action, now: () => number = Scheduler.now) {\n this.now = now;\n }\n\n /**\n * A getter method that returns a number representing the current time\n * (at the time this function was called) according to the scheduler's own\n * internal clock.\n * @return A number that represents the current time. May or may not\n * have a relation to wall-clock time. May or may not refer to a time unit\n * (e.g. milliseconds).\n */\n public now: () => number;\n\n /**\n * Schedules a function, `work`, for execution. May happen at some point in\n * the future, according to the `delay` parameter, if specified. May be passed\n * some context object, `state`, which will be passed to the `work` function.\n *\n * The given arguments will be processed an stored as an Action object in a\n * queue of actions.\n *\n * @param work A function representing a task, or some unit of work to be\n * executed by the Scheduler.\n * @param delay Time to wait before executing the work, where the time unit is\n * implicit and defined by the Scheduler itself.\n * @param state Some contextual data that the `work` function uses when called\n * by the Scheduler.\n * @return A subscription in order to be able to unsubscribe the scheduled work.\n */\n public schedule(work: (this: SchedulerAction, state?: T) => void, delay: number = 0, state?: T): Subscription {\n return new this.schedulerActionCtor(this, work).schedule(state, delay);\n }\n}\n", "import { Scheduler } from '../Scheduler';\nimport { Action } from './Action';\nimport { AsyncAction } from './AsyncAction';\nimport { TimerHandle } from './timerHandle';\n\nexport class AsyncScheduler extends Scheduler {\n public actions: Array> = [];\n /**\n * A flag to indicate whether the Scheduler is currently executing a batch of\n * queued actions.\n * @internal\n */\n public _active: boolean = false;\n /**\n * An internal ID used to track the latest asynchronous task such as those\n * coming from `setTimeout`, `setInterval`, `requestAnimationFrame`, and\n * others.\n * @internal\n */\n public _scheduled: TimerHandle | undefined;\n\n constructor(SchedulerAction: typeof Action, now: () => number = Scheduler.now) {\n super(SchedulerAction, now);\n }\n\n public flush(action: AsyncAction): void {\n const { actions } = this;\n\n if (this._active) {\n actions.push(action);\n return;\n }\n\n let error: any;\n this._active = true;\n\n do {\n if ((error = action.execute(action.state, action.delay))) {\n break;\n }\n } while ((action = actions.shift()!)); // exhaust the scheduler queue\n\n this._active = false;\n\n if (error) {\n while ((action = actions.shift()!)) {\n action.unsubscribe();\n }\n throw error;\n }\n }\n}\n", "import { AsyncAction } from './AsyncAction';\nimport { AsyncScheduler } from './AsyncScheduler';\n\n/**\n *\n * Async Scheduler\n *\n * Schedule task as if you used setTimeout(task, duration)\n *\n * `async` scheduler schedules tasks asynchronously, by putting them on the JavaScript\n * event loop queue. It is best used to delay tasks in time or to schedule tasks repeating\n * in intervals.\n *\n * If you just want to \"defer\" task, that is to perform it right after currently\n * executing synchronous code ends (commonly achieved by `setTimeout(deferredTask, 0)`),\n * better choice will be the {@link asapScheduler} scheduler.\n *\n * ## Examples\n * Use async scheduler to delay task\n * ```ts\n * import { asyncScheduler } from 'rxjs';\n *\n * const task = () => console.log('it works!');\n *\n * asyncScheduler.schedule(task, 2000);\n *\n * // After 2 seconds logs:\n * // \"it works!\"\n * ```\n *\n * Use async scheduler to repeat task in intervals\n * ```ts\n * import { asyncScheduler } from 'rxjs';\n *\n * function task(state) {\n * console.log(state);\n * this.schedule(state + 1, 1000); // `this` references currently executing Action,\n * // which we reschedule with new state and delay\n * }\n *\n * asyncScheduler.schedule(task, 3000, 0);\n *\n * // Logs:\n * // 0 after 3s\n * // 1 after 4s\n * // 2 after 5s\n * // 3 after 6s\n * ```\n */\n\nexport const asyncScheduler = new AsyncScheduler(AsyncAction);\n\n/**\n * @deprecated Renamed to {@link asyncScheduler}. Will be removed in v8.\n */\nexport const async = asyncScheduler;\n", "import { AsyncAction } from './AsyncAction';\nimport { Subscription } from '../Subscription';\nimport { QueueScheduler } from './QueueScheduler';\nimport { SchedulerAction } from '../types';\nimport { TimerHandle } from './timerHandle';\n\nexport class QueueAction extends AsyncAction {\n constructor(protected scheduler: QueueScheduler, protected work: (this: SchedulerAction, state?: T) => void) {\n super(scheduler, work);\n }\n\n public schedule(state?: T, delay: number = 0): Subscription {\n if (delay > 0) {\n return super.schedule(state, delay);\n }\n this.delay = delay;\n this.state = state;\n this.scheduler.flush(this);\n return this;\n }\n\n public execute(state: T, delay: number): any {\n return delay > 0 || this.closed ? super.execute(state, delay) : this._execute(state, delay);\n }\n\n protected requestAsyncId(scheduler: QueueScheduler, id?: TimerHandle, delay: number = 0): TimerHandle {\n // If delay exists and is greater than 0, or if the delay is null (the\n // action wasn't rescheduled) but was originally scheduled as an async\n // action, then recycle as an async action.\n\n if ((delay != null && delay > 0) || (delay == null && this.delay > 0)) {\n return super.requestAsyncId(scheduler, id, delay);\n }\n\n // Otherwise flush the scheduler starting with this action.\n scheduler.flush(this);\n\n // HACK: In the past, this was returning `void`. However, `void` isn't a valid\n // `TimerHandle`, and generally the return value here isn't really used. So the\n // compromise is to return `0` which is both \"falsy\" and a valid `TimerHandle`,\n // as opposed to refactoring every other instanceo of `requestAsyncId`.\n return 0;\n }\n}\n", "import { AsyncScheduler } from './AsyncScheduler';\n\nexport class QueueScheduler extends AsyncScheduler {\n}\n", "import { QueueAction } from './QueueAction';\nimport { QueueScheduler } from './QueueScheduler';\n\n/**\n *\n * Queue Scheduler\n *\n * Put every next task on a queue, instead of executing it immediately\n *\n * `queue` scheduler, when used with delay, behaves the same as {@link asyncScheduler} scheduler.\n *\n * When used without delay, it schedules given task synchronously - executes it right when\n * it is scheduled. However when called recursively, that is when inside the scheduled task,\n * another task is scheduled with queue scheduler, instead of executing immediately as well,\n * that task will be put on a queue and wait for current one to finish.\n *\n * This means that when you execute task with `queue` scheduler, you are sure it will end\n * before any other task scheduled with that scheduler will start.\n *\n * ## Examples\n * Schedule recursively first, then do something\n * ```ts\n * import { queueScheduler } from 'rxjs';\n *\n * queueScheduler.schedule(() => {\n * queueScheduler.schedule(() => console.log('second')); // will not happen now, but will be put on a queue\n *\n * console.log('first');\n * });\n *\n * // Logs:\n * // \"first\"\n * // \"second\"\n * ```\n *\n * Reschedule itself recursively\n * ```ts\n * import { queueScheduler } from 'rxjs';\n *\n * queueScheduler.schedule(function(state) {\n * if (state !== 0) {\n * console.log('before', state);\n * this.schedule(state - 1); // `this` references currently executing Action,\n * // which we reschedule with new state\n * console.log('after', state);\n * }\n * }, 0, 3);\n *\n * // In scheduler that runs recursively, you would expect:\n * // \"before\", 3\n * // \"before\", 2\n * // \"before\", 1\n * // \"after\", 1\n * // \"after\", 2\n * // \"after\", 3\n *\n * // But with queue it logs:\n * // \"before\", 3\n * // \"after\", 3\n * // \"before\", 2\n * // \"after\", 2\n * // \"before\", 1\n * // \"after\", 1\n * ```\n */\n\nexport const queueScheduler = new QueueScheduler(QueueAction);\n\n/**\n * @deprecated Renamed to {@link queueScheduler}. Will be removed in v8.\n */\nexport const queue = queueScheduler;\n", "import { AsyncAction } from './AsyncAction';\nimport { AnimationFrameScheduler } from './AnimationFrameScheduler';\nimport { SchedulerAction } from '../types';\nimport { animationFrameProvider } from './animationFrameProvider';\nimport { TimerHandle } from './timerHandle';\n\nexport class AnimationFrameAction extends AsyncAction {\n constructor(protected scheduler: AnimationFrameScheduler, protected work: (this: SchedulerAction, state?: T) => void) {\n super(scheduler, work);\n }\n\n protected requestAsyncId(scheduler: AnimationFrameScheduler, id?: TimerHandle, delay: number = 0): TimerHandle {\n // If delay is greater than 0, request as an async action.\n if (delay !== null && delay > 0) {\n return super.requestAsyncId(scheduler, id, delay);\n }\n // Push the action to the end of the scheduler queue.\n scheduler.actions.push(this);\n // If an animation frame has already been requested, don't request another\n // one. If an animation frame hasn't been requested yet, request one. Return\n // the current animation frame request id.\n return scheduler._scheduled || (scheduler._scheduled = animationFrameProvider.requestAnimationFrame(() => scheduler.flush(undefined)));\n }\n\n protected recycleAsyncId(scheduler: AnimationFrameScheduler, id?: TimerHandle, delay: number = 0): TimerHandle | undefined {\n // If delay exists and is greater than 0, or if the delay is null (the\n // action wasn't rescheduled) but was originally scheduled as an async\n // action, then recycle as an async action.\n if (delay != null ? delay > 0 : this.delay > 0) {\n return super.recycleAsyncId(scheduler, id, delay);\n }\n // If the scheduler queue has no remaining actions with the same async id,\n // cancel the requested animation frame and set the scheduled flag to\n // undefined so the next AnimationFrameAction will request its own.\n const { actions } = scheduler;\n if (id != null && id === scheduler._scheduled && actions[actions.length - 1]?.id !== id) {\n animationFrameProvider.cancelAnimationFrame(id as number);\n scheduler._scheduled = undefined;\n }\n // Return undefined so the action knows to request a new async id if it's rescheduled.\n return undefined;\n }\n}\n", "import { AsyncAction } from './AsyncAction';\nimport { AsyncScheduler } from './AsyncScheduler';\n\nexport class AnimationFrameScheduler extends AsyncScheduler {\n public flush(action?: AsyncAction): void {\n this._active = true;\n // The async id that effects a call to flush is stored in _scheduled.\n // Before executing an action, it's necessary to check the action's async\n // id to determine whether it's supposed to be executed in the current\n // flush.\n // Previous implementations of this method used a count to determine this,\n // but that was unsound, as actions that are unsubscribed - i.e. cancelled -\n // are removed from the actions array and that can shift actions that are\n // scheduled to be executed in a subsequent flush into positions at which\n // they are executed within the current flush.\n let flushId;\n if (action) {\n flushId = action.id;\n } else {\n flushId = this._scheduled;\n this._scheduled = undefined;\n }\n\n const { actions } = this;\n let error: any;\n action = action || actions.shift()!;\n\n do {\n if ((error = action.execute(action.state, action.delay))) {\n break;\n }\n } while ((action = actions[0]) && action.id === flushId && actions.shift());\n\n this._active = false;\n\n if (error) {\n while ((action = actions[0]) && action.id === flushId && actions.shift()) {\n action.unsubscribe();\n }\n throw error;\n }\n }\n}\n", "import { AnimationFrameAction } from './AnimationFrameAction';\nimport { AnimationFrameScheduler } from './AnimationFrameScheduler';\n\n/**\n *\n * Animation Frame Scheduler\n *\n * Perform task when `window.requestAnimationFrame` would fire\n *\n * When `animationFrame` scheduler is used with delay, it will fall back to {@link asyncScheduler} scheduler\n * behaviour.\n *\n * Without delay, `animationFrame` scheduler can be used to create smooth browser animations.\n * It makes sure scheduled task will happen just before next browser content repaint,\n * thus performing animations as efficiently as possible.\n *\n * ## Example\n * Schedule div height animation\n * ```ts\n * // html:
\n * import { animationFrameScheduler } from 'rxjs';\n *\n * const div = document.querySelector('div');\n *\n * animationFrameScheduler.schedule(function(height) {\n * div.style.height = height + \"px\";\n *\n * this.schedule(height + 1); // `this` references currently executing Action,\n * // which we reschedule with new state\n * }, 0, 0);\n *\n * // You will see a div element growing in height\n * ```\n */\n\nexport const animationFrameScheduler = new AnimationFrameScheduler(AnimationFrameAction);\n\n/**\n * @deprecated Renamed to {@link animationFrameScheduler}. Will be removed in v8.\n */\nexport const animationFrame = animationFrameScheduler;\n", "import { Observable } from '../Observable';\nimport { SchedulerLike } from '../types';\n\n/**\n * A simple Observable that emits no items to the Observer and immediately\n * emits a complete notification.\n *\n * Just emits 'complete', and nothing else.\n *\n * ![](empty.png)\n *\n * A simple Observable that only emits the complete notification. It can be used\n * for composing with other Observables, such as in a {@link mergeMap}.\n *\n * ## Examples\n *\n * Log complete notification\n *\n * ```ts\n * import { EMPTY } from 'rxjs';\n *\n * EMPTY.subscribe({\n * next: () => console.log('Next'),\n * complete: () => console.log('Complete!')\n * });\n *\n * // Outputs\n * // Complete!\n * ```\n *\n * Emit the number 7, then complete\n *\n * ```ts\n * import { EMPTY, startWith } from 'rxjs';\n *\n * const result = EMPTY.pipe(startWith(7));\n * result.subscribe(x => console.log(x));\n *\n * // Outputs\n * // 7\n * ```\n *\n * Map and flatten only odd numbers to the sequence `'a'`, `'b'`, `'c'`\n *\n * ```ts\n * import { interval, mergeMap, of, EMPTY } from 'rxjs';\n *\n * const interval$ = interval(1000);\n * const result = interval$.pipe(\n * mergeMap(x => x % 2 === 1 ? of('a', 'b', 'c') : EMPTY),\n * );\n * result.subscribe(x => console.log(x));\n *\n * // Results in the following to the console:\n * // x is equal to the count on the interval, e.g. (0, 1, 2, 3, ...)\n * // x will occur every 1000ms\n * // if x % 2 is equal to 1, print a, b, c (each on its own)\n * // if x % 2 is not equal to 1, nothing will be output\n * ```\n *\n * @see {@link Observable}\n * @see {@link NEVER}\n * @see {@link of}\n * @see {@link throwError}\n */\nexport const EMPTY = new Observable((subscriber) => subscriber.complete());\n\n/**\n * @param scheduler A {@link SchedulerLike} to use for scheduling\n * the emission of the complete notification.\n * @deprecated Replaced with the {@link EMPTY} constant or {@link scheduled} (e.g. `scheduled([], scheduler)`). Will be removed in v8.\n */\nexport function empty(scheduler?: SchedulerLike) {\n return scheduler ? emptyScheduled(scheduler) : EMPTY;\n}\n\nfunction emptyScheduled(scheduler: SchedulerLike) {\n return new Observable((subscriber) => scheduler.schedule(() => subscriber.complete()));\n}\n", "import { SchedulerLike } from '../types';\nimport { isFunction } from './isFunction';\n\nexport function isScheduler(value: any): value is SchedulerLike {\n return value && isFunction(value.schedule);\n}\n", "import { SchedulerLike } from '../types';\nimport { isFunction } from './isFunction';\nimport { isScheduler } from './isScheduler';\n\nfunction last(arr: T[]): T | undefined {\n return arr[arr.length - 1];\n}\n\nexport function popResultSelector(args: any[]): ((...args: unknown[]) => unknown) | undefined {\n return isFunction(last(args)) ? args.pop() : undefined;\n}\n\nexport function popScheduler(args: any[]): SchedulerLike | undefined {\n return isScheduler(last(args)) ? args.pop() : undefined;\n}\n\nexport function popNumber(args: any[], defaultValue: number): number {\n return typeof last(args) === 'number' ? args.pop()! : defaultValue;\n}\n", "export const isArrayLike = ((x: any): x is ArrayLike => x && typeof x.length === 'number' && typeof x !== 'function');", "import { isFunction } from \"./isFunction\";\n\n/**\n * Tests to see if the object is \"thennable\".\n * @param value the object to test\n */\nexport function isPromise(value: any): value is PromiseLike {\n return isFunction(value?.then);\n}\n", "import { InteropObservable } from '../types';\nimport { observable as Symbol_observable } from '../symbol/observable';\nimport { isFunction } from './isFunction';\n\n/** Identifies an input as being Observable (but not necessary an Rx Observable) */\nexport function isInteropObservable(input: any): input is InteropObservable {\n return isFunction(input[Symbol_observable]);\n}\n", "import { isFunction } from './isFunction';\n\nexport function isAsyncIterable(obj: any): obj is AsyncIterable {\n return Symbol.asyncIterator && isFunction(obj?.[Symbol.asyncIterator]);\n}\n", "/**\n * Creates the TypeError to throw if an invalid object is passed to `from` or `scheduled`.\n * @param input The object that was passed.\n */\nexport function createInvalidObservableTypeError(input: any) {\n // TODO: We should create error codes that can be looked up, so this can be less verbose.\n return new TypeError(\n `You provided ${\n input !== null && typeof input === 'object' ? 'an invalid object' : `'${input}'`\n } where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.`\n );\n}\n", "export function getSymbolIterator(): symbol {\n if (typeof Symbol !== 'function' || !Symbol.iterator) {\n return '@@iterator' as any;\n }\n\n return Symbol.iterator;\n}\n\nexport const iterator = getSymbolIterator();\n", "import { iterator as Symbol_iterator } from '../symbol/iterator';\nimport { isFunction } from './isFunction';\n\n/** Identifies an input as being an Iterable */\nexport function isIterable(input: any): input is Iterable {\n return isFunction(input?.[Symbol_iterator]);\n}\n", "import { ReadableStreamLike } from '../types';\nimport { isFunction } from './isFunction';\n\nexport async function* readableStreamLikeToAsyncGenerator(readableStream: ReadableStreamLike): AsyncGenerator {\n const reader = readableStream.getReader();\n try {\n while (true) {\n const { value, done } = await reader.read();\n if (done) {\n return;\n }\n yield value!;\n }\n } finally {\n reader.releaseLock();\n }\n}\n\nexport function isReadableStreamLike(obj: any): obj is ReadableStreamLike {\n // We don't want to use instanceof checks because they would return\n // false for instances from another Realm, like an + +
+

If you've got a basic understanding of QPython's features, welcome to start experiencing the fun of programming! Try the Hello World Tutorial to take your first step.

+ + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/en/index.html b/en/index.html new file mode 100644 index 0000000..acb7229 --- /dev/null +++ b/en/index.html @@ -0,0 +1,2651 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + Overview - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

QPython Project

+

QPython project is not only a powerful Python IDE for Android, but also an active technology community.

+

QPython Banner

+

AI-Enabled Python IDE for Android

+

QPython is your gateway to Python programming on Android. With an integrated Python interpreter, AI model engine, and mobile development toolchain, it empowers you to build web applications, perform scientific computing, and create intelligent apps — all from your mobile device.

+

Whether you're learning to code, building data science projects, or developing AI-powered applications, QPython provides a complete mobile programming solution with comprehensive developer resources and an active community to support your journey.

+
    +
  • Branches – Learn about the different QPython versions (IDE, Community, Plus) and choose the right one for your needs
  • +
  • Updates – Stay informed about the latest features, improvements, and release notes
  • +
+
+

Getting Started

+

How to start quickly? Just follow these steps:

+ +

Programming Guide

+

QPython not only provides basic Python interface support, but more importantly, it also enables you to call Android APIs using Python through the QSL4A interface.

+
    +
  • Python Standard Library – For general Python syntax and built-in libraries
  • +
  • QSL4A API – For accessing Android device features (camera, sensors, SMS, etc.) from Python
  • +
  • QPYPI Guide – For installing additional Python packages
  • +
  • Editor Guide – For using the built-in code editor
  • +
  • External API – For integrating with external applications
  • +
+
+

Download Resources

+

Latest Version v4.0.0:

+
    +
  • External Storage Access: Users can now save Python scripts directly to external storage devices, greatly improving file management flexibility.
  • +
  • QSL4A Enhanced: Improved QSL4A functionality.
  • +
  • +

    Community & Courses: Optimized community and courses modules, providing clearer information and more convenient navigation for accessing learning resources and support.

    +
  • +
  • +

    Google Drive

    +
  • +
  • 微信网盘
  • +
+

Community & Feedback

+ +

Follow Us

+ +
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/en/qpypi-guide/index.html b/en/qpypi-guide/index.html new file mode 100644 index 0000000..ccb4372 --- /dev/null +++ b/en/qpypi-guide/index.html @@ -0,0 +1,2617 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + QPYPI - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

QPYPI

+

You can extend your QPython capabilities by installing packages.

+

Package Installation Support

+

Pure Python Packages

+

QPython supports Python packages developed with pure Python. You can install these packages directly using pip install through QPython's PIP Client or QPYPI on dashboard.

+

Pre-compiled Packages

+

If some packages (or their dependencies) are developed with Rust/C/C++, QPython cannot support them directly because there is no compiler toolchain support on QPython. However, the QPython team has pre-compiled some commonly used packages and published them in QPython's QPYPI/Extensions for users to install easily.

+

Installing Pre-compiled Packages

+

You can install pre-compiled packages in the following ways:

+
    +
  1. Through QPython App: Install directly from QPYPI or Extensions within the QPython app
  2. +
  3. Through PyPI: Visit https://pypi.org/user/qpythonx/ to view available packages
  4. +
  5. Via pip command:
  6. +
  7. pip install xxx-qpython - Packages with the -qpython suffix
  8. +
  9. pip install xxx-aipy - Packages with the -aipy suffix (typically AI/ML related packages)
  10. +
+
+

Note: We usually add one of these suffixes based on the package's intended use case.

+
+

Requesting New Packages

+

If you need a package that is not currently supported:

+
    +
  • Raise an issue in the qpython.org project
  • +
  • The QPython team will consider pre-compiling and adding it to the repository
  • +
+

For more ways to get help and engage with the community, see the Community & Feedback section.

+
+

Note: Because of different computer architectures, we cannot guarantee that QPYPI includes all packages from PyPI.

+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/en/qpython-x/index.html b/en/qpython-x/index.html new file mode 100644 index 0000000..47e6e75 --- /dev/null +++ b/en/qpython-x/index.html @@ -0,0 +1,2578 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Branches - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + +

QPython Branches

+

QPython is the Python engine for Android. It contains amazing features such as Python interpreter, runtime environment, editor, QPYI and integrated SL4A. It makes it easy for you to use Python on Android. And it's FREE.

+

QPython already has millions of users worldwide and it is also an open source project.

+

For different usage scenarios, QPython has several branches:

+

QPython

+

Standard Edition: Optimized for AI performance and universal app store compatibility

+

The main version available on Google Play and other app stores. This version focuses on AI features, making it easier for users to learn and use Python in the AI era.

+

Key Features: +- AI-powered coding assistance and learning tools +- Offline Python 3.12 interpreter: no Internet is required to run Python programs +- Supports multiple project types: console, SL4A, webapp +- Convenient QR code reader for transferring codes to your phone +- QPYPI and custom repository for prebuilt wheel packages +- Easy-to-use editor with syntax highlighting +- Good documentation and community support

+

Permissions: Requires only basic phone permissions for installation.

+

Download: Available on Google Play and major app stores.

+

QPython+

+

Community Edition: Openly supports various community-driven features; available on select app stores.

+

The community open-source version (in planning and preparation). This version is designed for contributors who want to participate in QPython project development and supports customization for different manufacturers.

+

Key Features: +- Community-driven development +- Support for vendor customization +- More flexible configuration options +- Open for contributors to join development

+

Permissions: Requires only basic phone permissions for installation.

+

Download: Will be available on Google Play and major app stores.

+

Note: This version is currently in planning and preparation phase. Stay tuned for updates!

+

QPython Plus

+

QPython+ Full Access Edition: Grants complete permissions to call all Android interfaces. Download link available exclusively via the official cloud drive.

+

A special version with extended permissions that provides maximum control over the device. This version is NOT published on app stores due to its sensitive permission requirements.

+

Key Features: +- Full SL4A API access including sensitive features +- SMS/Call control APIs +- Advanced system integration +- Maximum device control capabilities

+

Permissions: Requires extensive permissions including: +- Bluetooth +- Location (GPS) +- Read/Send SMS +- Call phone +- Camera and microphone +- System settings +- And other sensitive permissions

+

Download: Not available on app stores. Distributed through special channels only.

+

Important: QPython will not use these permissions in background without your knowledge. If you get exceptions while using SL4A APIs, please check whether the relevant permissions are enabled in system settings.

+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/en/qsl4a/connectivity/contacts/index.html b/en/qsl4a/connectivity/contacts/index.html new file mode 100644 index 0000000..a2425f6 --- /dev/null +++ b/en/qsl4a/connectivity/contacts/index.html @@ -0,0 +1,2659 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + Contacts API - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

Contacts API

+

Access and manage device contacts.

+

Contact Picking

+

pickContact()

+

Display a list of contacts to pick from.

+
pickContact()
+
+

Returns: Intent with contact URI

+

pickPhone()

+

Display a list of phone numbers to pick from.

+
pickPhone()
+
+

Returns: Selected phone number string

+

Contact Queries

+

contactsGet()

+

Get all contacts.

+
contactsGet(attributes=None)
+
+

Parameters: +- attributes (list, optional): Specific attributes to retrieve

+

Returns: List of contact JSONObject

+

contactsGetById()

+

Get a contact by ID.

+
contactsGetById(id, attributes=None)
+
+

Parameters: +- id (int): Contact ID +- attributes (list, optional): Specific attributes to retrieve

+

Returns: JSONObject contact data

+

contactsGetCount()

+

Get the total number of contacts.

+
contactsGetCount()
+
+

Returns: Integer count

+

contactsGetIds()

+

Get all contact IDs.

+
contactsGetIds()
+
+

Returns: List of contact ID integers

+

contactsGetAttributes()

+

Get all possible contact attributes.

+
contactsGetAttributes()
+
+

Returns: List of attribute names

+

Content Queries

+

queryContent()

+

Query content resolver with custom parameters.

+
queryContent(uri, attributes=None, selection=None, selectionArgs=None, order=None)
+
+

Parameters: +- uri (str): Content URI +- attributes (list, optional): Attributes to retrieve +- selection (str, optional): WHERE clause +- selectionArgs (list, optional): Selection arguments +- order (str, optional): ORDER BY clause

+

Returns: List of JSONObject results

+

queryAttributes()

+

Get attributes for a content URI.

+
queryAttributes(uri)
+
+

Parameters: +- uri (str): Content URI

+

Returns: JSONArray of attribute names

+

Usage Example

+
import androidhelper
+
+droid = androidhelper.Android()
+
+# Pick a contact
+contact_uri = droid.pickContact().result
+print(f"Selected contact: {contact_uri}")
+
+# Pick a phone number
+phone = droid.pickPhone().result
+print(f"Selected phone: {phone}")
+
+# Get all contacts
+contacts = droid.contactsGet().result
+print(f"Total contacts: {len(contacts)}")
+
+# Get contact by ID
+contact = droid.contactsGetById(1).result
+print(f"Contact: {contact}")
+
+# Get contact attributes
+attrs = droid.contactsGetAttributes().result
+print(f"Available attributes: {attrs}")
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/en/qsl4a/connectivity/ftp/index.html b/en/qsl4a/connectivity/ftp/index.html new file mode 100644 index 0000000..6f4cbd4 --- /dev/null +++ b/en/qsl4a/connectivity/ftp/index.html @@ -0,0 +1,2570 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + FTP Server API - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + +

FTP Server API

+

Start and manage a built-in FTP server on the device.

+

FTP Server Methods

+

ftpStart()

+

Start the FTP server.

+
ftpStart()
+
+

Returns: Array containing IP address and port [ip, port]

+

ftpStop()

+

Stop the FTP server.

+
ftpStop()
+
+

ftpIsRunning()

+

Check if FTP server is running.

+
ftpIsRunning()
+
+

Returns: True if running

+

ftpGet()

+

Get FTP server IP address.

+
ftpGet()
+
+

Returns: Array with IP address and port

+

ftpSet()

+

Configure FTP server settings.

+
ftpSet(port=None, rootDir=None, username=None, password=None)
+
+

Parameters: +- port (int, optional): Server port +- rootDir (str, optional): Root directory to serve +- username (str, optional): Login username +- password (str, optional): Login password

+

Returns: JSONObject with current settings

+

ftpStatus()

+

Get FTP server status.

+
ftpStatus()
+
+

Returns: String status description

+

Usage Example

+
import androidhelper
+
+droid = androidhelper.Android()
+
+# Configure FTP server
+droid.ftpSet(
+    port=2121,
+    rootDir="/sdcard",
+    username="admin",
+    password="secret"
+)
+
+# Start FTP server
+info = droid.ftpStart().result
+print(f"FTP running at {info[0]}:{info[1]}")
+
+# Check status
+if droid.ftpIsRunning().result:
+    print("FTP server is running")
+
+# Get server info
+server_info = droid.ftpGet().result
+print(f"Server: {server_info}")
+
+# Stop when done
+droid.ftpStop()
+
+

Note: Connect to the FTP server using any FTP client with the provided credentials.

+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/en/qsl4a/connectivity/location/index.html b/en/qsl4a/connectivity/location/index.html new file mode 100644 index 0000000..f1548fe --- /dev/null +++ b/en/qsl4a/connectivity/location/index.html @@ -0,0 +1,2824 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Location - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

Location API

+

Access GPS and network location services.

+

Methods

+

startLocating()

+

Start location updates.

+
startLocating(minUpdateTime=60000, minUpdateDistance=30, updateGnssStatus=False)
+
+

Parameters: +- minUpdateTime (int): Minimum time between updates in milliseconds (default: 60000) +- minUpdateDistance (float): Minimum distance for update in meters (default: 30) +- updateGnssStatus (bool): Enable GNSS status updates (default: False)

+

stopLocating()

+

Stop location updates.

+
stopLocating()
+
+

readLocation()

+

Get last known location.

+
readLocation()
+
+

Returns: Location data dict

+

getLastKnownLocation()

+

Get cached location.

+
getLastKnownLocation()
+
+

Returns: Location from all providers

+

geocode()

+

Convert address to coordinates.

+
geocode(address, maxResults=1)
+
+

Location Provider Methods)

+

locationProviders()

+

Get available location providers on the phone.

+
locationProviders()
+
+

Returns: List of available provider names (e.g., ['gps', 'network'])

+

locationProviderEnabled()

+

Check if a specific location provider is enabled.

+
locationProviderEnabled(provider)
+
+

Parameters: +- provider (str): Provider name (e.g., 'gps', 'network')

+

Returns: True if enabled, False otherwise

+

readGnssStatus()

+

Read Global Navigation Satellite System status (requires Android 8+).

+
readGnssStatus()
+
+

Returns: JSONArray containing GNSS satellite information

+

Usage Example

+
import androidhelper
+import time
+
+droid = androidhelper.Android()
+
+# Start location updates
+droid.startLocating(minUpdateTime=5000, minUpdateDistance=1, updateGnssStatus=False)
+
+# Wait for fix
+time.sleep(10)
+
+# Get location
+loc = droid.readLocation().result
+if loc:
+    lat = loc['latitude']
+    lon = loc['longitude']
+    print(f"Location: {lat}, {lon}")
+
+droid.stopLocating()
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/en/qsl4a/connectivity/phone/index.html b/en/qsl4a/connectivity/phone/index.html new file mode 100644 index 0000000..df184f9 --- /dev/null +++ b/en/qsl4a/connectivity/phone/index.html @@ -0,0 +1,3027 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + Phone API - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + +

Phone API

+

Control phone calls and retrieve phone information.

+

Phone State Tracking

+

startTrackingPhoneState()

+

Start tracking phone state changes. Generates 'phone' events.

+
startTrackingPhoneState()
+
+

readPhoneState()

+

Read the current phone state.

+
readPhoneState()
+
+

Returns: Bundle with phone state and incoming number

+

stopTrackingPhoneState()

+

Stop tracking phone state.

+
stopTrackingPhoneState()
+
+

Making Calls

+

phoneCall()

+

Call a contact/phone number by URI.

+
phoneCall(uri)
+
+

Parameters: +- uri (str): Contact URI or phone number URI

+

phoneCallNumber()

+

Call a phone number directly.

+
phoneCallNumber(phone_number)
+
+

Parameters: +- phone_number (str): Phone number to call

+

phoneDial()

+

Dial a number (opens dialer without calling).

+
phoneDial(uri)
+
+

Parameters: +- uri (str): Contact URI or phone number URI

+

phoneDialNumber()

+

Dial a phone number (opens dialer without calling).

+
phoneDialNumber(phone_number)
+
+

Parameters: +- phone_number (str): Phone number

+

Cell Location

+

getCellLocation()

+

Get the current cell location.

+
getCellLocation()
+
+

Returns: JSONObject with cell location data

+

getAllCellsLocation()

+

Get all cell locations (for dual SIM devices).

+
getAllCellsLocation()
+
+

Returns: JSONArray of cell locations

+

Network Information

+

getNetworkOperator()

+

Get the MCC+MNC of the current operator.

+
getNetworkOperator()
+
+

Returns: String (e.g., '310260')

+

getNetworkOperatorName()

+

Get the name of the current operator.

+
getNetworkOperatorName()
+
+

Returns: String (e.g., 'T-Mobile')

+

getNetworkType()

+

Get the current network type.

+
getNetworkType()
+
+

Returns: String describing radio technology (e.g., 'LTE', 'UMTS', 'GSM')

+

getPhoneType()

+

Get the phone type.

+
getPhoneType()
+
+

Returns: String (e.g., 'GSM', 'CDMA', 'SIP')

+

SIM Information

+

getSimCountryIso()

+

Get the ISO country code for the SIM.

+
getSimCountryIso()
+
+

Returns: String (e.g., 'us')

+

getSimOperator()

+

Get the MCC+MNC of the SIM operator.

+
getSimOperator()
+
+

Returns: String (e.g., '310260')

+

getSimOperatorName()

+

Get the SIM operator name.

+
getSimOperatorName()
+
+

Returns: String (e.g., 'T-Mobile')

+

getSimSerialNumber()

+

Get the SIM serial number.

+
getSimSerialNumber()
+
+

Returns: String SIM serial number

+

getSimState()

+

Get the SIM card state.

+
getSimState()
+
+

Returns: String describing SIM state

+

getSubscriberId()

+

Get the subscriber ID.

+
getSubscriberId()
+
+

Returns: String subscriber ID

+

Voice Mail

+

getVoiceMailAlphaTag()

+

Get the voice mail alpha tag.

+
getVoiceMailAlphaTag()
+
+

Returns: String voice mail tag

+

getVoiceMailNumber()

+

Get the voice mail number.

+
getVoiceMailNumber()
+
+

Returns: String voice mail number

+

Device Information

+

getDeviceId()

+

Get the device ID (IMEI for GSM). Deprecated.

+
getDeviceId()
+
+

Returns: String device ID

+

getDeviceSoftwareVersion()

+

Get the device software version.

+
getDeviceSoftwareVersion()
+
+

Returns: String software version

+

getLine1Number()

+

Get the line 1 phone number.

+
getLine1Number()
+
+

Returns: String phone number

+

checkNetworkRoaming()

+

Check if connected to roaming network.

+
checkNetworkRoaming()
+
+

Returns: True if roaming

+

Cell Info

+

getAllCellInfo()

+

Get information about all cells.

+
getAllCellInfo()
+
+

Returns: List of cell information

+

setDataEnabled()

+

Enable or disable mobile data.

+
setDataEnabled(enabled)
+
+

Parameters: +- enabled (bool): True to enable, False to disable

+

Usage Example

+
import androidhelper
+
+droid = androidhelper.Android()
+
+# Get network info
+operator = droid.getNetworkOperatorName().result
+print(f"Operator: {operator}")
+
+network_type = droid.getNetworkType().result
+print(f"Network: {network_type}")
+
+# Get SIM info
+sim_state = droid.getSimState().result
+print(f"SIM: {sim_state}")
+
+# Get phone number
+line1 = droid.getLine1Number().result
+print(f"Phone: {line1}")
+
+# Track phone state
+droid.startTrackingPhoneState()
+print("Tracking phone state...")
+droid.stopTrackingPhoneState()
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/en/qsl4a/connectivity/signalstrength/index.html b/en/qsl4a/connectivity/signalstrength/index.html new file mode 100644 index 0000000..f43cb19 --- /dev/null +++ b/en/qsl4a/connectivity/signalstrength/index.html @@ -0,0 +1,2541 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + Signal Strength API - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

Signal Strength API

+

Monitor cellular and wireless signal strength.

+

Signal Strength Methods

+

startTrackingSignalStrengths()

+

Start tracking signal strength changes. Generates 'signal_strengths' events.

+
startTrackingSignalStrengths()
+
+

stopTrackingSignalStrengths()

+

Stop tracking signal strengths.

+
stopTrackingSignalStrengths()
+
+

readSignalStrengths()

+

Read the current signal strengths.

+
readSignalStrengths()
+
+

Returns: Bundle with signal strength data

+

getTelephoneSignalStrengthLevel()

+

Get the telephone signal strength as a level (0-4).

+
getTelephoneSignalStrengthLevel()
+
+

Returns: Integer level (0=none, 1=poor, 2=fair, 3=good, 4=excellent)

+

getTelephoneSignalStrengthDetail()

+

Get detailed telephone signal strength information.

+
getTelephoneSignalStrengthDetail()
+
+

Returns: String with detailed signal info

+

Usage Example

+
import androidhelper
+import time
+
+droid = androidhelper.Android()
+
+# Start tracking signal strength
+droid.startTrackingSignalStrengths()
+
+# Wait for signal updates
+time.sleep(5)
+
+# Read current signal strength
+signal = droid.readSignalStrengths().result
+print(f"Signal: {signal}")
+
+# Get level directly
+level = droid.getTelephoneSignalStrengthLevel().result
+print(f"Signal level: {level}/4")
+
+droid.stopTrackingSignalStrengths()
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/en/qsl4a/connectivity/sms/index.html b/en/qsl4a/connectivity/sms/index.html new file mode 100644 index 0000000..092be10 --- /dev/null +++ b/en/qsl4a/connectivity/sms/index.html @@ -0,0 +1,2778 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SMS - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

SMS API

+

Send and receive SMS messages.

+

Methods

+

smsSend()

+

Send SMS message.

+
smsSend(destinationAddress, text)
+
+

Parameters: +- destinationAddress (str): Phone number +- text (str): Message text

+

smsGetMessageCount()

+

Get message count.

+
smsGetMessageCount(unreadOnly=False, folder="inbox")
+
+

smsGetMessageIds()

+

Get message IDs.

+
smsGetMessageIds(unreadOnly=False, folder="inbox")
+
+

smsGetMessages()

+

Get message details.

+
smsGetMessages(unreadOnly=False, folder="inbox", attributes=None)
+
+

smsGetMessageById()

+

Get a specific message by ID.

+
smsGetMessageById(id, attributes=None)
+
+

Parameters: +- id (int): Message ID +- attributes (list, optional): Specific attributes to retrieve

+

Returns: Message data dict

+

smsGetAttributes()

+

Get available SMS message attributes.

+
smsGetAttributes()
+
+

Returns: List of available attribute names

+

smsDeleteMessage()

+

Delete message.

+
smsDeleteMessage(id)
+
+

smsMarkMessageRead()

+

Mark message as read.

+
smsMarkMessageRead(ids, read=True)
+
+

Usage Example

+
import androidhelper
+
+droid = androidhelper.Android()
+
+# Send SMS
+droid.smsSend("+1234567890", "Hello from QPython!")
+
+# Get unread messages
+messages = droid.smsGetMessages(unreadOnly=True).result
+for msg in messages:
+    print(f"From: {msg['address']}, Text: {msg['body']}")
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/en/qsl4a/connectivity/wifi/index.html b/en/qsl4a/connectivity/wifi/index.html new file mode 100644 index 0000000..6f8666a --- /dev/null +++ b/en/qsl4a/connectivity/wifi/index.html @@ -0,0 +1,3091 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + WiFi - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

WiFi API

+

Control WiFi adapter and get connection information.

+

Adapter Control

+

checkWifiState()

+

Check if WiFi is enabled.

+
checkWifiState()
+
+

Returns: True if WiFi is enabled, False otherwise

+

toggleWifiState()

+

Turn WiFi on or off.

+
toggleWifiState(enabled=None)
+
+

Parameters: +- enabled (bool): True to enable, False to disable, None to toggle

+

Returns: True if operation succeeded

+

wifiStartScan()

+

Start scanning for available WiFi networks.

+
wifiStartScan()
+
+

wifiGetScanResults()

+

Get list of discovered WiFi networks.

+
wifiGetScanResults()
+
+

Returns: List of access point information

+

Connection Management

+

wifiGetConnectionInfo()

+

Get detailed connection information.

+
wifiGetConnectionInfo()
+
+

Returns: Dict with connection details including SSID, BSSID, IP address

+

getConnectedInfo()

+

Get connected WiFi network info (simplified).

+
getConnectedInfo()
+
+

Returns: Dict with SSID, BSSID, signal strength

+

getDhcpInfo()

+

Get DHCP information for current connection.

+
getDhcpInfo(ipConvertToString=True)
+
+

Parameters: +- ipConvertToString (bool): Convert IP addresses to string format (default: True)

+

Returns: Dict with DHCP info including IP, gateway, DNS

+

wifiDisconnect()

+

Disconnect from current WiFi network.

+
wifiDisconnect()
+
+

wifiReconnect()

+

Reconnect to the current network.

+
wifiReconnect()
+
+

wifiReassociate()

+

Reassociate with the current access point.

+
wifiReassociate()
+
+

Hotspot

+

wifiGetApState()

+

Get WiFi AP (hotspot) state.

+
wifiGetApState()
+
+

Returns: Hotspot state

+

WiFi Locks

+

wifiLockAcquireFull()

+

Acquire a full WiFi lock (keeps WiFi active even when screen is off).

+
wifiLockAcquireFull()
+
+

wifiLockAcquireScanOnly()

+

Acquire a scan-only WiFi lock.

+
wifiLockAcquireScanOnly()
+
+

wifiLockRelease()

+

Release the WiFi lock.

+
wifiLockRelease()
+
+

Usage Example

+
import androidhelper
+import time
+
+droid = androidhelper.Android()
+
+# Check WiFi state
+if droid.checkWifiState().result:
+    print("WiFi is enabled")
+else:
+    print("Enabling WiFi...")
+    droid.toggleWifiState(True)
+    time.sleep(2)
+
+# Start scanning
+droid.wifiStartScan()
+time.sleep(3)
+
+# Get scan results
+networks = droid.wifiGetScanResults().result
+for network in networks:
+    print(f"SSID: {network.get('SSID')}, Signal: {network.get('level')} dBm")
+
+# Get connection info
+info = droid.wifiGetConnectionInfo().result
+if info:
+    print(f"Connected to: {info.get('ssid')}")
+    print(f"BSSID: {info.get('bssid')}")
+    print(f"IP: {info.get('ip_address')}")
+
+# Get DHCP info
+dhcp = droid.getDhcpInfo().result
+if dhcp:
+    print(f"Gateway: {dhcp.get('gateway')}")
+    print(f"DNS: {dhcp.get('dns1')}")
+
+# Get simplified connected info
+connected = droid.getConnectedInfo().result
+print(f"SSID: {connected.get('ssid')}, Signal: {connected.get('level')} dBm")
+
+# Disconnect and reconnect
+droid.wifiDisconnect()
+time.sleep(1)
+droid.wifiReconnect()
+
+# Reassociate with access point
+droid.wifiReassociate()
+
+# Check hotspot state
+ap_state = droid.wifiGetApState().result
+print(f"Hotspot state: {ap_state}")
+
+# Acquire WiFi lock for background operation
+droid.wifiLockAcquireFull()
+# ... do work ...
+droid.wifiLockRelease()
+
+# Or use scan-only lock for lighter background operation
+droid.wifiLockAcquireScanOnly()
+# ... do scanning work ...
+droid.wifiLockRelease()
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/en/qsl4a/core/android-base/index.html b/en/qsl4a/core/android-base/index.html new file mode 100644 index 0000000..967295a --- /dev/null +++ b/en/qsl4a/core/android-base/index.html @@ -0,0 +1,2950 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Android Base - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

Android Base Class

+

The Android class is the core of QSL4A, providing the connection to the Android runtime and RPC mechanism.

+

Module Import

+
# Full featured (recommended)
+import androidhelper
+droid = androidhelper.Android()
+
+# Minimal version (quick, single instance)
+import android
+droid = android.droid
+
+

Class: Android

+

Constructor

+
Android(addr=None)
+
+

Parameters: +- addr (tuple, optional): (HOST, PORT) address. If None, uses environment variables AP_HOST and AP_PORT

+

Environment Variables: +- AP_HOST - Server host address +- AP_PORT - Server port +- AP_HANDSHAKE - Authentication token

+

Core Methods

+

_rpc()

+

Internal RPC method for calling Android functions.

+
_rpc(method, *args)
+
+

Parameters: +- method (str): Method name to call +- *args: Variable arguments for the method

+

Returns: Result namedtuple with fields: +- id (int): Request ID +- result: Method return value +- error (str or None): Error message if failed

+

getattr()

+

Dynamic method dispatcher. All Android methods are accessed via attribute lookup.

+
# These are equivalent:
+droid.makeToast("Hello")
+droid._rpc("makeToast", "Hello")
+
+

Standalone Functions (android.py)

+

When using the minimal android module:

+

jsla()

+

Send JSON-RPC request and return raw response.

+
jsla(method, *params)
+
+

Returns: JSON string response

+

rsla()

+

Send request and return result only.

+
rsla(method, *params)
+
+

Returns: Result value from RPC call

+

esla()

+

Send request, raise exception on error.

+
esla(method, *params)
+
+

Raises: Exception if error field is not None

+

nsla()

+

Send request and return Result namedtuple.

+
nsla(method, *params)
+
+

Returns: Result namedtuple

+

Usage Examples

+

Basic Connection

+
import androidhelper
+
+# Connect to Android runtime
+droid = androidhelper.Android()
+
+# Check connection by showing toast
+droid.makeToast("Connected!")
+
+

Error Handling

+
result = droid.someMethod()
+if result.error:
+    print(f"Error: {result.error}")
+else:
+    print(f"Success: {result.result}")
+
+

Direct RPC Call

+
# Call any method directly
+result = droid._rpc("makeToast", "Hello World")
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/en/qsl4a/core/events/index.html b/en/qsl4a/core/events/index.html new file mode 100644 index 0000000..647571f --- /dev/null +++ b/en/qsl4a/core/events/index.html @@ -0,0 +1,3318 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Event System - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

Event System

+

QSL4A provides an event system for handling asynchronous events from Android, such as sensor updates, location changes, and custom broadcasts.

+

Event Basics

+

Events are stored in a buffer and can be polled or waited for. Each event has: +- name: Event type/name +- data: Event payload data +- time: Timestamp

+

Event Methods

+

eventClearBuffer()

+

Clear all pending events from the buffer.

+
eventClearBuffer()
+
+

Returns: None

+

eventPoll()

+

Poll for events in the buffer.

+
eventPoll(number_of_events=1)
+
+

Parameters: +- number_of_events (int): Maximum events to retrieve (default: 1)

+

Returns: List of event objects

+

eventWait()

+

Wait for any event.

+
eventWait(timeout=None)
+
+

Parameters: +- timeout (int, optional): Timeout in seconds. None = wait forever

+

Returns: Event object or None if timeout

+

eventWaitFor()

+

Wait for a specific event.

+
eventWaitFor(eventName, timeout=None)
+
+

Parameters: +- eventName (str): Name of event to wait for +- timeout (int, optional): Timeout in seconds

+

Returns: Event object or None if timeout

+

eventPost()

+

Post a custom event.

+
eventPost(name, data, enqueue=None)
+
+

Parameters: +- name (str): Event name +- data: Event data (any type) +- enqueue (bool, optional): Add to queue if True

+

receiveEvent()

+

Receive event (blocking).

+
receiveEvent()
+
+

Returns: Event object

+

Broadcast Events

+

Register for system broadcast events.

+

eventRegisterForBroadcast()

+

Register to receive broadcast events.

+
eventRegisterForBroadcast(category, enqueue=True)
+
+

Parameters: +- category (str): Broadcast category/action +- enqueue (bool): Add to event queue

+

eventUnregisterForBroadcast()

+

Unregister from broadcast events.

+
eventUnregisterForBroadcast(category)
+
+

eventGetBrodcastCategories()

+

Get registered broadcast categories.

+
eventGetBrodcastCategories()
+
+

Returns: List of registered categories

+

Event Dispatcher)

+

startEventDispatcher()

+

Opens up a socket where you can read for events posted.)

+
startEventDispatcher(port=0)
+
+

Parameters: +- port (int, optional): Port to listen on (default: 0 = auto-select)

+

Returns: Port number being listened on

+

stopEventDispatcher()

+

Stops the event server.)

+
stopEventDispatcher()
+
+

Deprecated Methods

+

rpcPostEvent()

+

Post an event to the event queue. (Deprecated, use eventPost)

+
rpcPostEvent(name, data)
+
+

Parameters: +- name (str): Event name +- data: Event data

+

Usage Examples

+

Basic Event Polling

+
import androidhelper
+
+droid = androidhelper.Android()
+
+# Clear old events
+droid.eventClearBuffer()
+
+# Poll for events
+events = droid.eventPoll(5).result
+for event in events:
+    print(f"Event: {event['name']}, Data: {event['data']}")
+
+

Waiting for Events

+
# Wait for any event with timeout
+event = droid.eventWait(timeout=10).result
+if event:
+    print(f"Got event: {event['name']}")
+
+

Waiting for Specific Event

+
# Wait for sensor event
+event = droid.eventWaitFor('screen', timeout=5).result
+if event:
+    print(f"Screen event: {event['data']}")
+
+

Posting Custom Events

+
# Post custom event
+droid.eventPost('my_event', {'key': 'value'})
+
+# Wait for it
+event = droid.eventWaitFor('my_event', timeout=1).result
+
+

Broadcast Receiver

+
# Register for screen on/off events
+droid.eventRegisterForBroadcast('android.intent.action.SCREEN_ON')
+droid.eventRegisterForBroadcast('android.intent.action.SCREEN_OFF')
+
+# Wait for screen events
+while True:
+    event = droid.receiveEvent().result
+    if event['name'] == 'android.intent.action.SCREEN_ON':
+        print("Screen turned on")
+    elif event['name'] == 'android.intent.action.SCREEN_OFF':
+        print("Screen turned off")
+
+

Sensor Event Handling

+
# Start sensing
+droid.startSensingTimed(1, 250)
+
+# Process sensor events
+for _ in range(100):
+    event = droid.eventWait(timeout=1).result
+    if event and event['name'] == 'sensors':
+        data = event['data']
+        print(f"Accel: {data['xforce']}, {data['yforce']}, {data['zforce']}")
+
+droid.stopSensing()
+
+

Common Event Types

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Event NameDescriptionSource
sensorsSensor data updatestartSensing*
locationGPS location updatestartLocating
phonePhone state changestartTrackingPhoneState
signalSignal strength changestartTrackingSignalStrength
screenScreenshot readyfullGetScreenShot
dialogDialog responsedialogShow*
+

Event Data Structure

+
{
+    'name': 'event_name',
+    'data': {
+        # Event-specific data
+    },
+    'time': 1234567890  # Timestamp
+}
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/en/qsl4a/core/intent/index.html b/en/qsl4a/core/intent/index.html new file mode 100644 index 0000000..9b29607 --- /dev/null +++ b/en/qsl4a/core/intent/index.html @@ -0,0 +1,3600 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Intent System - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

Intent System

+

Android Intents are used to start activities, send broadcasts, and communicate between apps. QSL4A provides full Intent support through the Intent module.

+

Module Import

+
import androidhelper
+don = androidhelper.Android()
+
+

Intent Constants

+

Access via droid.Intent:

+

Actions

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ConstantValueUsage
ACTION_MAINandroid.intent.action.MAINApp entry point
ACTION_VIEWandroid.intent.action.VIEWView content
ACTION_EDITandroid.intent.action.EDITEdit content
ACTION_PICKandroid.intent.action.PICKPick item
ACTION_SENDandroid.intent.action.SENDShare content
ACTION_SEARCHandroid.intent.action.SEARCHSearch
+

Flags

+ + + + + + + + + + + + + + + + + + + + + + + + + +
ConstantValueUsage
FLAG_ACTIVITY_NEW_TASK268435456Start new task
FLAG_ACTIVITY_CLEAR_TASK32768Clear task stack
FLAG_ACTIVITY_NEW_DOCUMENT524288New document mode
+

Extras

+ + + + + + + + + + + + + + + + + + + + + + + + + +
ConstantUsage
EXTRA_TEXTText content
EXTRA_STREAMFile URI
EXTRA_SUBJECTSubject line
EXTRA_EMAILEmail address
+

Core Methods

+

makeIntent()

+

Create an Intent object.

+
makeIntent(action, uri=None, type=None, extras=None, categories=None,
+           packagename=None, classname=None, flags=None)
+
+

Parameters: +- action (str): Intent action (e.g., droid.Intent.ACTION_VIEW) +- uri (str, optional): Data URI +- type (str, optional): MIME type +- extras (dict, optional): Extra data +- categories (list, optional): Intent categories +- packagename (str, optional): Target package +- classname (str, optional): Target class +- flags (int, optional): Intent flags

+

Returns: Intent object

+

startActivityIntent()

+

Start an Activity with an Intent.

+
startActivityIntent(intent, wait=None)
+
+

Parameters: +- intent: Intent object from makeIntent() +- wait (bool, optional): Block until activity closes

+

startActivityForResultIntent()

+

Start activity and wait for result.

+
startActivityForResultIntent(intent)
+
+

Returns: Activity result

+

sendBroadcastIntent()

+

Send a broadcast.

+
sendBroadcastIntent(intent)
+
+

view()

+

View content by URI.

+
view(uri, type=None, extras=None)
+
+

pick()

+

Pick content from URI.

+
pick(uri)
+
+

Common Intent Methods)

+

scanBarcode()

+

Launch the barcode scanner.

+
scanBarcode()
+
+

Returns: Scanned barcode string

+

send()

+

Send content via share intent.

+
send(type, content)
+
+

Parameters: +- type (str): MIME type +- content (str): Content to share

+

sendText()

+

Send text content.

+
sendText(text)
+
+

Parameters: +- text (str): Text to send

+

sendEmail()

+

Send an email.

+
sendEmail(to, subject, body, attachment=None)
+
+

Parameters: +- to (str or list): Recipient email address(es) +- subject (str): Email subject +- body (str): Email body +- attachment (str, optional): Attachment file path

+

pathToUri()

+

Convert file path to content URI.

+
pathToUri(path)
+
+

Parameters: +- path (str): File path

+

Returns: Content URI string

+

openFile()

+

Open a file with appropriate app.

+
openFile(path)
+
+

Parameters: +- path (str): File path to open

+

sendFile()

+

Send a file via share intent.

+
sendFile(path)
+
+

Parameters: +- path (str): File path to send

+

getPathType()

+

Get the MIME type for a file path.

+
getPathType(path)
+
+

Parameters: +- path (str): File path

+

Returns: MIME type string

+

viewMap()

+

Open map at a location.

+
viewMap(latitude, longitude)
+
+

Parameters: +- latitude (float): Latitude +- longitude (float): Longitude

+

viewContacts()

+

Open the contacts app.

+
viewContacts()
+
+ +

Perform a web search.

+
search(query)
+
+

Parameters: +- query (str): Search query

+

viewHtml()

+

View HTML content.

+
viewHtml(content, encoding=None)
+
+

Parameters: +- content (str): HTML content +- encoding (str, optional): Character encoding

+

webViewShow()

+

Display web content in WebView. Deprecated, use viewHtml.

+
webViewShow(url)
+
+

Parameters: +- url (str): Web page URL

+

editorOpen()

+

Open a text editor.

+
editorOpen(path=None, create=False)
+
+

Parameters: +- path (str, optional): File path to edit +- create (bool, optional): Create if doesn't exist

+

Helper Class: Uri

+

Create URI objects for Intents:

+
from androidhelper.Intent import Uri
+
+uri = Uri("file:///sdcard/test.txt")
+
+

Usage Examples

+

Open Web Page

+
intent = droid.makeIntent(
+    action=droid.Intent.ACTION_VIEW,
+    uri="http://www.example.com"
+).result
+droid.startActivityIntent(intent)
+
+

Share Text

+
intent = droid.makeIntent(
+    action=droid.Intent.ACTION_SEND,
+    extras={
+        droid.Intent.EXTRA_TEXT: "Hello from QPython!",
+        droid.Intent.EXTRA_SUBJECT: "Test"
+    },
+    type="text/plain"
+).result
+droid.startActivityIntent(intent)
+
+

Open File

+
intent = droid.makeIntent(
+    action=droid.Intent.ACTION_VIEW,
+    uri="file:///sdcard/document.pdf",
+    type="application/pdf"
+).result
+droid.startActivityIntent(intent)
+
+

Pick Contact

+
result = droid.pickContact()
+contact_uri = result.result
+
+

Send Email

+
intent = droid.makeIntent(
+    action=droid.Intent.ACTION_SEND,
+    extras={
+        droid.Intent.EXTRA_EMAIL: ["test@example.com"],
+        droid.Intent.EXTRA_SUBJECT: "Hello",
+        droid.Intent.EXTRA_TEXT: "Message body"
+    },
+    type="message/rfc822"
+).result
+droid.startActivityIntent(intent)
+
+

Open App

+
intent = droid.makeIntent(
+    action=droid.Intent.ACTION_MAIN,
+    packagename="com.android.settings",
+    classname="com.android.settings.Settings"
+).result
+droid.startActivityIntent(intent)
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/en/qsl4a/hardware/bluetooth/index.html b/en/qsl4a/hardware/bluetooth/index.html new file mode 100644 index 0000000..8be029d --- /dev/null +++ b/en/qsl4a/hardware/bluetooth/index.html @@ -0,0 +1,3213 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Bluetooth - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

Bluetooth API

+

Control Bluetooth adapter and communicate with Bluetooth devices.

+

Adapter Control

+

toggleBluetoothState()

+

Turn Bluetooth on/off.

+
toggleBluetoothState(enabled=None, prompt=True)
+
+

Parameters: +- enabled (bool): True=on, False=off, None=toggle +- prompt (bool): Show user prompt

+

checkBluetoothState()

+

Check if Bluetooth is enabled.

+
checkBluetoothState()
+
+

Returns: True/False

+

GetLocalName()

+

Get Bluetooth device name.

+
GetLocalName()
+
+

SetLocalName()

+

Set Bluetooth device name.

+
SetLocalName(name)
+
+

GetScanMode()

+

Get discoverability mode.

+
GetScanMode()
+
+

Returns: +- -1: Disabled +- 0: Non-discoverable, non-connectable +- 1: Connectable, non-discoverable +- 3: Connectable and discoverable

+

MakeDiscoverable()

+

Make device discoverable.

+
MakeDiscoverable(duration=300)
+
+

Parameters: +- duration (int): Seconds to be discoverable

+

Discovery

+

DiscoveryStart()

+

Start device discovery.

+
DiscoveryStart()
+
+

DiscoveryCancel()

+

Cancel discovery.

+
DiscoveryCancel()
+
+

GetReceivedDevices()

+

Get discovered devices.

+
GetReceivedDevices()
+
+

Returns: List of device info dicts

+

GetBondedDevices()

+

Get paired devices.

+
GetBondedDevices()
+
+

Returns: List of paired device info

+

Connection

+

Connect()

+

Connect to a device.

+
Connect(uuid="457807c0-4897-11df-9879-0800200c9a66", address=None)
+
+

Parameters: +- uuid (str): Service UUID +- address (str): Device address (None = show picker)

+

Returns: True if successful

+

Accept()

+

Accept incoming connection.

+
Accept(uuid="457807c0-4897-11df-9879-0800200c9a66", timeout=0)
+
+

ActiveConnections()

+

Check active connections.

+
ActiveConnections()
+
+

Stop()

+

Disconnect.

+
Stop(connID=None)
+
+

Communication

+

Write()

+

Send ASCII data.

+
Write(ascii, connID="")
+
+

WriteBinary()

+

Send binary data (base64 encoded).

+
WriteBinary(base64, connID=None)
+
+

Read()

+

Read ASCII data.

+
Read(bufferSize=4096, connID=None)
+
+

ReadBinary()

+

Read binary data.

+
ReadBinary(bufferSize=4096, connID=None)
+
+

ReadLine()

+

Read line.

+
ReadLine(connID=None)
+
+

ReadReady()

+

Check if data available.

+
ReadReady(connID=None)
+
+

Usage Example

+
import androidhelper
+
+droid = androidhelper.Android()
+
+# Enable Bluetooth
+droid.toggleBluetoothState(True)
+
+# Get paired devices
+devices = droid.GetBondedDevices().result
+for dev in devices:
+    print(f"{dev['name']}: {dev['address']}")
+
+# Connect to device
+droid.Connect(address="00:11:22:33:44:55")
+
+# Send data
+droid.Write("Hello Bluetooth!")
+
+# Read response
+data = droid.Read(1024).result
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/en/qsl4a/hardware/camera/index.html b/en/qsl4a/hardware/camera/index.html new file mode 100644 index 0000000..97f2b25 --- /dev/null +++ b/en/qsl4a/hardware/camera/index.html @@ -0,0 +1,3057 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Camera - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

Camera API

+

Capture photos and record video.

+

Photo Capture

+

takePicture()

+

Take a photo using the default camera.

+
takePicture(path=None)
+
+

Parameters: +- path (str, optional): Save path. If None, returns image data

+

Returns: Image path or image data

+

cameraCapturePicture()

+

Capture picture with advanced camera controls.

+
cameraCapturePicture(targetPath=None, cameraId=0, useAutoFocus=True)
+
+

Parameters: +- targetPath (str, optional): Save path +- cameraId (int): Camera to use (0 = back, 1 = front) +- useAutoFocus (bool): Enable auto focus

+

Returns: Captured image path

+

cameraSetTorchMode()

+

Control camera flashlight/torch.

+
cameraSetTorchMode(enabled)
+
+

Parameters: +- enabled (bool): True to turn on, False to turn off

+

Returns: True if successful

+

imageReaderGetScreenShot()

+

Capture screenshot.

+
imageReaderGetScreenShot(path=None, delayMilliSec=1000)
+
+

Parameters: +- path (str, optional): Save path +- delayMilliSec (int): Delay before capture

+

Video Recording

+

takeVideo()

+

Record video using default settings.

+
takeVideo(path=None, quality=1)
+
+

Parameters: +- path (str, optional): Save path +- quality (int): Video quality (0-4) + - 0: 160x120 + - 1: 320x240 + - 2: 352x288 + - 3: 640x480 + - 4: 800x480

+

recorderCaptureVideo()

+

Capture video with advanced controls.

+
recorderCaptureVideo(targetPath=None, duration=10, cameraId=0, quality=8)
+
+

Parameters: +- targetPath (str, optional): Save path +- duration (int): Recording duration in seconds (default: 10) +- cameraId (int): Camera to use (0 = back, 1 = front) +- quality (int): Video quality (0-8, higher is better)

+

Returns: Video file path

+

recordAudio()

+

Record audio.

+
recordAudio()
+
+

Returns: Audio file path

+

Screen Recording

+

recorderStartScreenRecord()

+

Start screen recording.

+
recorderStartScreenRecord(path=None, audio=1, targetPixels=None,
+                          frameRate=30, bitRate=None, rotation=False,
+                          autoStart=True)
+
+

Parameters: +- path (str): Save path +- audio (int): Audio source (0=none, 1=mic, 2=internal) +- targetPixels (int): Resolution +- frameRate (int): FPS +- bitRate (int): Bitrate +- rotation (bool): Rotate output +- autoStart (bool): Start immediately

+

recorderStart()

+

Start recording.

+
recorderStart()
+
+

recorderPause()

+

Pause recording.

+
recorderPause()
+
+

recorderResume()

+

Resume recording.

+
recorderResume()
+
+

Audio Volume Detection

+

recorderSoundVolumeDetect()

+

Start volume detection.

+
recorderSoundVolumeDetect(interval=100)
+
+

recorderSoundVolumeGetDb()

+

Get current volume in dB.

+
recorderSoundVolumeGetDb()
+
+

Usage Example

+
import androidhelper
+
+droid = androidhelper.Android()
+
+# Take photo with default camera
+photo_path = droid.takePicture("/sdcard/photo.jpg").result
+print(f"Photo saved: {photo_path}")
+
+# Take photo with front camera and auto focus
+camera_path = droid.cameraCapturePicture("/sdcard/selfie.jpg", cameraId=1, useAutoFocus=True).result
+print(f"Front camera photo: {camera_path}")
+
+# Control flashlight
+droid.cameraSetTorchMode(True)  # Turn on flashlight
+
+# Record video with default settings
+video_path = droid.takeVideo("/sdcard/video.mp4", quality=3).result
+print(f"Video saved: {video_path}")
+
+# Record video with advanced controls
+video_path = droid.recorderCaptureVideo("/sdcard/movie.mp4", duration=30, cameraId=0, quality=8).result
+
+# Screenshot
+ss_path = droid.imageReaderGetScreenShot("/sdcard/screenshot.png", 500).result
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/en/qsl4a/hardware/recorder/index.html b/en/qsl4a/hardware/recorder/index.html new file mode 100644 index 0000000..090a444 --- /dev/null +++ b/en/qsl4a/hardware/recorder/index.html @@ -0,0 +1,2877 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Audio/Recorder - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

Audio Recorder API

+

Record audio from microphone and device screen.

+

Audio Recording

+

recordAudio()

+

Record audio from microphone.

+
recordAudio()
+
+

Returns: Path to recorded audio file

+

recorderStartMicrophone()

+

Start recording from microphone to a specific file.

+
recorderStartMicrophone(targetPath=None)
+
+

Parameters: +- targetPath (str, optional): Path to save the recording

+

Screen Recording

+

recorderStartScreenRecord()

+

Start screen recording with audio.

+
recorderStartScreenRecord(path=None, audio=1, targetPixels=None,
+                          frameRate=30, bitRate=None, rotation=False,
+                          autoStart=True)
+
+

Parameters: +- path (str): Save path for video file +- audio (int): Audio source (0=none, 1=mic, 2=internal audio) +- targetPixels (int): Target resolution in pixels +- frameRate (int): Frames per second (default: 30) +- bitRate (int): Video bitrate +- rotation (bool): Rotate output video +- autoStart (bool): Start recording immediately

+

Returns: Result of operation

+

recorderStart()

+

Start the screen recording (when autoStart=False).

+
recorderStart()
+
+

recorderPause()

+

Pause ongoing screen recording.

+
recorderPause()
+
+

recorderResume()

+

Resume paused screen recording.

+
recorderResume()
+
+

Audio Volume Detection

+

recorderSoundVolumeDetect()

+

Start monitoring sound volume level.

+
recorderSoundVolumeDetect(interval=100)
+
+

Parameters: +- interval (int): Detection interval in milliseconds (default: 100)

+

recorderSoundVolumeGetDb()

+

Get current sound volume in decibels.

+
recorderSoundVolumeGetDb()
+
+

Returns: Current volume level in dB

+

Usage Example

+
import androidhelper
+import time
+
+droid = androidhelper.Android()
+
+# Record audio from microphone
+print("Recording audio...")
+audio_path = droid.recordAudio().result
+print(f"Saved to: {audio_path}")
+
+# Record microphone to specific file
+droid.recorderStartMicrophone("/sdcard/my_recording.mp3")
+time.sleep(5)
+droid.recorderStop()
+
+# Record screen with audio
+print("Starting screen recording...")
+droid.recorderStartScreenRecord(
+    path="/sdcard/screen_record.mp4",
+    audio=1,
+    frameRate=30,
+    autoStart=True
+)
+time.sleep(10)
+droid.recorderStop()
+
+# Monitor sound volume
+droid.recorderSoundVolumeDetect(interval=100)
+time.sleep(3)
+volume = droid.recorderSoundVolumeGetDb().result
+print(f"Current volume: {volume} dB")
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/en/qsl4a/hardware/usbserial/index.html b/en/qsl4a/hardware/usbserial/index.html new file mode 100644 index 0000000..88a23df --- /dev/null +++ b/en/qsl4a/hardware/usbserial/index.html @@ -0,0 +1,2691 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + USB Host Serial API - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

USB Host Serial API

+

Communicate with USB serial devices (requires USB OTG support and Android 3.1+).

+

USB Serial Methods)

+

usbHostSerialOpen()

+

Open a connection to a USB serial device.

+
usbHostSerialOpen(device, baudRate=9600)
+
+

Parameters: +- device (str): USB device path or identifier +- baudRate (int): Baud rate (default: 9600)

+

Returns: True if opened successfully

+

usbHostSerialClose()

+

Close the USB serial connection.

+
usbHostSerialClose()
+
+

usbHostSerialRead()

+

Read data from USB serial.

+
usbHostSerialRead(bufferSize=1024)
+
+

Parameters: +- bufferSize (int): Maximum bytes to read (default: 1024)

+

Returns: String read data

+

usbHostSerialWrite()

+

Write data to USB serial.

+
usbHostSerialWrite(data)
+
+

Parameters: +- data (str): String data to write

+

usbHostSerialAvailable()

+

Check if data is available to read.

+
usbHostSerialAvailable()
+
+

Returns: Number of bytes available

+

Configuration Methods)

+

usbHostSerialSetBaudRate()

+

Set the baud rate.

+
usbHostSerialSetBaudRate(baudRate)
+
+

Parameters: +- baudRate (int): Baud rate

+

usbHostSerialSetDataBits()

+

Set data bits (5, 6, 7, or 8).

+
usbHostSerialSetDataBits(dataBits)
+
+

Parameters: +- dataBits (int): Data bits (5-8)

+

usbHostSerialSetStopBits()

+

Set stop bits (1, 1.5, or 2).

+
usbHostSerialSetStopBits(stopBits)
+
+

Parameters: +- stopBits (float): Stop bits (1, 1.5, or 2)

+

usbHostSerialSetParity()

+

Set parity (none, odd, even, mark, space).

+
usbHostSerialSetParity(parity)
+
+

Parameters: +- parity (str): Parity mode ('none', 'odd', 'even', 'mark', 'space')

+

usbHostSerialSetFlowControl()

+

Set flow control (none, hardware, software).

+
usbHostSerialSetFlowControl(flowControl)
+
+

Parameters: +- flowControl (str): Flow control mode ('none', 'hardware', 'software')

+

usbHostSerialReadHex()

+

Read data as hex string.

+
usbHostSerialReadHex(bufferSize=1024)
+
+

Parameters: +- bufferSize (int): Maximum bytes to read

+

Returns: Hex string

+

usbHostSerialWriteHex()

+

Write data from hex string.

+
usbHostSerialWriteHex(hexString)
+
+

Parameters: +- hexString (str): Hex string to write

+

Usage Example

+
import androidhelper
+
+droid = androidhelper.Android()
+
+# Open USB serial connection
+if droid.usbHostSerialOpen("/dev/bus/usb/001/001", 115200).result:
+    print("USB serial opened")
+
+    # Write data
+    droid.usbHostSerialWrite("AT\r")
+
+    # Read response
+    response = droid.usbHostSerialRead(1024).result
+    print(f"Response: {response}")
+
+    # Or use hex
+    droid.usbHostSerialWriteHex("41540D0A")  # "AT\r\n"
+
+    # Close connection
+    droid.usbHostSerialClose()
+
+

Note: USB serial requires: +- Android 3.1+ (API 12) +- USB OTG cable/adapter +- USB host mode support on device +- Compatible serial device

+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/en/qsl4a/hardware/webcam/index.html b/en/qsl4a/hardware/webcam/index.html new file mode 100644 index 0000000..8847f26 --- /dev/null +++ b/en/qsl4a/hardware/webcam/index.html @@ -0,0 +1,2581 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + Webcam API - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

Webcam API

+

Stream video from the device camera using MJPEG.

+

MJPEG Stream Methods

+

webcamStart()

+

Start an MJPEG stream from the webcam.

+
webcamStart(resolutionLevel=0, jpegQuality=20, port=0)
+
+

Parameters: +- resolutionLevel (int): Resolution level (default: 0) +- jpegQuality (int): JPEG quality 1-100 (default: 20) +- port (int): Port number (default: 0 = auto)

+

Returns: Tuple of (address, port) for the stream

+

webcamAdjustQuality()

+

Adjust the quality of an active webcam stream.

+
webcamAdjustQuality(resolutionLevel=0, jpegQuality=20)
+
+

Parameters: +- resolutionLevel (int): Resolution level +- jpegQuality (int): JPEG quality 1-100

+

webcamStop()

+

Stop the webcam stream.

+
webcamStop()
+
+

Camera Preview Methods

+

cameraStartPreview()

+

Start camera preview mode with event generation.

+
cameraStartPreview(resolutionLevel=0, jpegQuality=20, filepath=None)
+
+

Parameters: +- resolutionLevel (int): Resolution level (default: 0) +- jpegQuality (int): JPEG quality (default: 20) +- filepath (str, optional): File path to save preview frames

+

Returns: True if successful

+

Note: Generates 'preview' events with frame data.

+

cameraStopPreview()

+

Stop the camera preview.

+
cameraStopPreview()
+
+

Usage Example

+
import androidhelper
+import time
+
+droid = androidhelper.Android()
+
+# Start webcam stream
+stream_info = droid.webcamStart(
+    resolutionLevel=0,
+    jpegQuality=30,
+    port=8080
+).result
+print(f"Stream available at {stream_info[0]}:{stream_info[1]}")
+
+# Adjust quality while streaming
+time.sleep(5)
+droid.webcamAdjustQuality(resolutionLevel=1, jpegQuality=50)
+
+# Stop when done
+droid.webcamStop()
+
+# Or use preview mode
+print("Starting preview...")
+droid.cameraStartPreview()
+
+# Wait for preview events
+for i in range(10):
+    event = droid.eventWait(timeout=1).result
+    if event and event['name'] == 'preview':
+        print(f"Got preview frame: {event['data']}")
+
+droid.cameraStopPreview()
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/en/qsl4a/index.html b/en/qsl4a/index.html new file mode 100644 index 0000000..8aa0e94 --- /dev/null +++ b/en/qsl4a/index.html @@ -0,0 +1,2834 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Overview - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

QSL4A (Scripting Layer for Android) API Documentation

+

QSL4A is QPython's scripting layer for Android, allowing you to control Android device features using Python.

+

Quick Start

+
import androidhelper
+
+droid = androidhelper.Android()
+
+# Show a toast message
+droid.makeToast('Hello QPython!')
+
+# Vibrate the device
+droid.vibrate(500)
+
+# Get battery level (start monitoring first)
+import time
+droid.batteryStartMonitoring()
+time.sleep(0.5)  # Wait for data
+battery = droid.readBatteryData().result
+print(f"Battery: {battery['level']}%")
+
+

Documentation Structure

+

Core Modules

+ +

UI Components

+ +

System

+ +

Hardware

+ +

Connectivity

+ +

Storage

+ +

Media

+ +

Special Features

+ +

Result Object

+

Most QSL4A methods return a Result namedtuple with: +- id - Request ID +- result - The actual result data +- error - Error message if failed

+
result = droid.getClipboard()
+if result.error is None:
+    print(result.result)
+else:
+    print(f"Error: {result.error}")
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/en/qsl4a/media/image/index.html b/en/qsl4a/media/image/index.html new file mode 100644 index 0000000..af87e33 --- /dev/null +++ b/en/qsl4a/media/image/index.html @@ -0,0 +1,2803 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Image Processing - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + +

Image Processing API

+

Compress and process images.

+

Image Compression

+

imageCompress()

+

Compress image file.

+
imageCompress(srcPath, destPath, targetByteSize=0, targetWidth=0, targetHeight=0)
+
+

Parameters: +- srcPath (str): Source image path +- destPath (str): Output path +- targetByteSize (int): Target file size in bytes (0 = no limit) +- targetWidth (int): Target width (0 = original) +- targetHeight (int): Target height (0 = original)

+

Returns: Compressed image path

+

Screenshot

+

imageReaderGetScreenShot()

+

Capture screen.

+
imageReaderGetScreenShot(path=None, delayMilliSec=1000)
+
+

Parameters: +- path (str): Save path +- delayMilliSec (int): Delay before capture

+

Returns: Screenshot path

+

Video Playback

+

videoPlay()

+

Play video file in fullscreen mode.

+
videoPlay(path, wait=True)
+
+

Parameters: +- path (str): Video file path +- wait (bool): Wait for playback to complete (default: True)

+

Barcode Scanning

+

scanBarcodeFromImage()

+

Scan barcode/QR code from image file.

+
scanBarcodeFromImage(path, compressRatio=0, x=0, y=0, width=0, height=0)
+
+

Parameters: +- path (str): Image file path +- compressRatio (int): Compression ratio (0 = no compression) +- x, y, width, height (int): Region to scan (0 = full image)

+

Returns: Scanned barcode content

+

Usage Example

+
import androidhelper
+
+droid = androidhelper.Android()
+
+# Take screenshot
+ss_path = droid.imageReaderGetScreenShot("/sdcard/screenshot.png", 500).result
+
+# Compress image
+compressed = droid.imageCompress(
+    "/sdcard/large_photo.jpg",
+    "/sdcard/compressed.jpg",
+    targetByteSize=102400,  # Target ~100KB
+    targetWidth=1920,
+    targetHeight=1080
+).result
+print(f"Saved: {compressed}")
+
+# Play video
+video_path = "/sdcard/movie.mp4"
+droid.videoPlay(video_path, wait=True)
+
+# Scan barcode from image
+result = droid.scanBarcodeFromImage("/sdcard/qr_code.png").result
+print(f"Barcode: {result}")
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/en/qsl4a/media/mediaplayer/index.html b/en/qsl4a/media/mediaplayer/index.html new file mode 100644 index 0000000..4a6a0bf --- /dev/null +++ b/en/qsl4a/media/mediaplayer/index.html @@ -0,0 +1,3052 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Media Player - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

Media Player API

+

Control audio and video playback.

+

Playback Control

+

mediaPlay()

+

Play media file.

+
mediaPlay(url, tag="default", play=True)
+
+

Parameters: +- url (str): Media file path or URL +- tag (str): Player identifier +- play (bool): Auto-start playback

+

mediaPlayStart()

+

Start playback.

+
mediaPlayStart(tag="default")
+
+

mediaPlayPause()

+

Pause playback.

+
mediaPlayPause(tag="default")
+
+

mediaPlayClose()

+

Close player.

+
mediaPlayClose(tag="default")
+
+

mediaPlaySeek()

+

Seek to position.

+
mediaPlaySeek(msec, tag="default")
+
+

Parameters: +- msec (int): Position in milliseconds

+

mediaPlaySetLooping()

+

Set loop mode.

+
mediaPlaySetLooping(enabled, tag="default")
+
+

Player Info

+

mediaPlayInfo()

+

Get playback info.

+
mediaPlayInfo(tag="default")
+
+

Returns: Dict with duration, position, etc.

+

mediaIsPlaying()

+

Check if playing.

+
mediaIsPlaying(tag="default")
+
+

Returns: True/False

+

mediaPlayList()

+

List active players.

+
mediaPlayList()
+
+

Volume Control

+

getMediaVolume()

+

Get media volume.

+
getMediaVolume()
+
+

Returns: Volume level (0-15)

+

getMaxMediaVolume()

+

Get max media volume.

+
getMaxMediaVolume()
+
+

getRingerVolume()

+

Get ringer volume.

+
getRingerVolume()
+
+

getMaxRingerVolume()

+

Get max ringer volume.

+
getMaxRingerVolume()
+
+

Video Playback

+

videoPlay()

+

Play video fullscreen.

+
videoPlay(path, wait=True)
+
+

Parameters: +- path (str): Video file path +- wait (bool): Wait for playback to complete

+

Usage Example

+
import androidhelper
+
+droid = androidhelper.Android()
+
+# Play audio
+droid.mediaPlay("/sdcard/music.mp3", tag="music")
+
+# Check status
+if droid.mediaIsPlaying("music").result:
+    info = droid.mediaPlayInfo("music").result
+    print(f"Playing: {info}")
+
+# Seek to 30 seconds
+droid.mediaPlaySeek(30000, "music")
+
+# Close
+droid.mediaPlayClose("music")
+
+# Play video
+droid.videoPlay("/sdcard/movie.mp4", wait=True)
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/en/qsl4a/special/cipher/index.html b/en/qsl4a/special/cipher/index.html new file mode 100644 index 0000000..1ba012a --- /dev/null +++ b/en/qsl4a/special/cipher/index.html @@ -0,0 +1,2929 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Cipher - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

Cipher API

+

Encryption and decryption utilities for secure data storage.

+

Initialization

+

cipherInit()

+

Initialize the cipher with encryption key and algorithm.

+
cipherInit(key, algorithm="AES/CBC/PKCS5Padding", encodingFormat="", initialVector="")
+
+

Parameters: +- key (str or bytes): Encryption key +- algorithm (str): Cipher algorithm (default: "AES/CBC/PKCS5Padding") +- encodingFormat (str): Encoding format +- initialVector (str or bytes): Initial vector for encryption

+

Returns: Initialization result

+

Note: Must be called before any encrypt/decrypt operations.

+

Encryption Methods

+

encryptString()

+

Encrypt a string.

+
encryptString(plainText)
+
+

Parameters: +- plainText (str): Text to encrypt

+

Returns: Encrypted string

+

encryptBytes()

+

Encrypt bytes data.

+
encryptBytes(data)
+
+

Parameters: +- data (bytes): Data to encrypt

+

Returns: Encrypted bytes

+

encryptStringToFile()

+

Encrypt string to file.

+
encryptStringToFile(plainText, filePath)
+
+

Parameters: +- plainText (str): Text to encrypt +- filePath (str): Output file path

+

encryptBytesToFile()

+

Encrypt bytes to file.

+
encryptBytesToFile(data, filePath)
+
+

Decryption Methods

+

decryptString()

+

Decrypt to string.

+
decryptString(cipherText)
+
+

Parameters: +- cipherText (str): Encrypted text

+

Returns: Decrypted string

+

decryptBytes()

+

Decrypt to bytes.

+
decryptBytes(data)
+
+

Returns: Decrypted bytes

+

decryptFileToString()

+

Decrypt file to string.

+
decryptFileToString(filePath)
+
+

decryptFileToBytes()

+

Decrypt file to bytes.

+
decryptFileToBytes(filePath)
+
+

decryptFile()

+

Decrypt file to file.

+
decryptFile(srcPath, destPath)
+
+

Usage Example

+
import androidhelper
+
+droid = androidhelper.Android()
+
+# Initialize cipher with your encryption key
+droid.cipherInit("my_secret_key_1234")
+
+# Encrypt string
+encrypted = droid.encryptString("Secret message!").result
+print(f"Encrypted: {encrypted}")
+
+# Decrypt string
+decrypted = droid.decryptString(encrypted).result
+print(f"Decrypted: {decrypted}")
+
+# Encrypt to file
+droid.encryptStringToFile("My secret data", "/sdcard/secret.dat")
+
+# Decrypt from file
+data = droid.decryptFileToString("/sdcard/secret.dat").result
+print(data)
+
+# Initialize with custom algorithm and IV
+droid.cipherInit(
+    key="my_key",
+    algorithm="AES/CBC/PKCS5Padding",
+    initialVector="0123456789abcdef"
+)
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/en/qsl4a/special/pgptai/index.html b/en/qsl4a/special/pgptai/index.html new file mode 100644 index 0000000..2a1f809 --- /dev/null +++ b/en/qsl4a/special/pgptai/index.html @@ -0,0 +1,2763 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + PGPT AI - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + +

PGPT AI API

+

Speech-to-text and AI services integration.

+

Prerequisites

+
pip install pgptAI
+
+

Speech Recognition

+

speechToText()

+

Convert speech to text.

+
speechToText(RecordSecond=10, AmrFile=None, Language=None)
+
+

Parameters: +- RecordSecond (int): Recording duration in seconds +- AmrFile (str, optional): Existing audio file path +- Language (str, optional): Language code ('en', 'zh')

+

Returns: Transcribed text

+

Text to Speech

+

textToSpeech()

+

Convert text to speech and optionally play it.

+
textToSpeech(Text, AutoPlay=True, WavFile=None, VoiceName=None)
+
+

Parameters: +- Text (str): Text to convert to speech +- AutoPlay (bool): Automatically play the generated audio (default: True) +- WavFile (str, optional): Path to save the WAV file. If None, uses temporary file +- VoiceName (str, optional): Voice name to use (e.g., 'en-US-JennyNeural', 'zh-CN-XiaoxiaoNeural')

+

Returns: Dict with speech synthesis result including: + - text: The input text + - url: URL to download the audio file + - WavFile: Path to the saved WAV file (if saved locally)

+

Configuration

+

The API uses configuration from /storage/emulated/0/Android/data/org.qpython.qpy/files/apigpt.conf:

+
[speech]
+speech_key = your_api_key
+
+

Default voice settings: +- English: en-US-JennyNeural +- Chinese: zh-CN-XiaoxiaoNeural

+

Usage Example

+
import androidhelper
+
+droid = androidhelper.Android()
+
+# Record and transcribe
+print("Recording for 5 seconds...")
+text = droid.speechToText(RecordSecond=5, Language='en').result
+print(f"You said: {text}")
+
+# Transcribe existing file
+text = droid.speechToText(AmrFile="/sdcard/recording.amr").result
+print(f"Transcription: {text}")
+
+# Text to speech
+droid.textToSpeech("Hello, this is a test message.", AutoPlay=True, Language='en')
+
+# Text to speech with custom voice and save to file
+result = droid.textToSpeech(
+    "Welcome to QPython!",
+    AutoPlay=False,
+    WavFile="/sdcard/welcome.wav",
+    VoiceName="en-US-JennyNeural"
+).result
+print(f"Audio saved to: {result.get('WavFile')}")
+
+

Class Usage

+
from androidhelper.pgptai import pgptai
+import androidhelper
+
+droid = androidhelper.Android()
+ai = pgptai(droid)
+
+# Use speech recognition
+text = ai.speechToText(RecordSecond=10)
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/en/qsl4a/storage/clipboard/index.html b/en/qsl4a/storage/clipboard/index.html new file mode 100644 index 0000000..4219422 --- /dev/null +++ b/en/qsl4a/storage/clipboard/index.html @@ -0,0 +1,2617 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Clipboard - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + +

Clipboard API

+

Copy and paste text to system clipboard.

+

Methods

+

setClipboard()

+

Copy text to clipboard.

+
setClipboard(text)
+
+

Parameters: +- text (str): Text to copy

+

Returns: True if success

+

getClipboard()

+

Get text from clipboard.

+
getClipboard()
+
+

Returns: Clipboard text

+

Usage Example

+
import androidhelper
+
+droid = androidhelper.Android()
+
+# Copy to clipboard
+droid.setClipboard("Hello from QPython!")
+
+# Paste from clipboard
+text = droid.getClipboard().result
+print(f"Clipboard: {text}")
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/en/qsl4a/storage/documentfile/index.html b/en/qsl4a/storage/documentfile/index.html new file mode 100644 index 0000000..d8cb1ef --- /dev/null +++ b/en/qsl4a/storage/documentfile/index.html @@ -0,0 +1,3142 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + DocumentFile - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

DocumentFile API

+

File operations with SAF (Storage Access Framework) support for Android 4.4+.

+

Directory Operations

+

documentFileMkdir()

+

Create directory.

+
documentFileMkdir(Dir)
+
+

Parameters: +- Dir (str): Directory path

+

Returns: True if success

+

documentFileListFiles()

+

List files in directory.

+
documentFileListFiles(Folder)
+
+

Returns: List of files

+

File Operations

+

documentFileExists()

+

Check if file or directory exists.

+
documentFileExists(path)
+
+

Parameters: +- path (str): File or directory path

+

Returns: True if exists, False otherwise

+

documentFileIsFile()

+

Check if path is a file.

+
documentFileIsFile(path)
+
+

Parameters: +- path (str): Path to check

+

Returns: True if file, False if not a file, None if not exists

+

documentFileIsDirectory()

+

Check if path is a directory.

+
documentFileIsDirectory(path)
+
+

Parameters: +- path (str): Path to check

+

Returns: True if directory, False if not a directory, None if not exists

+

documentFileDelete()

+

Delete file or directory.

+
documentFileDelete(FileOrTree)
+
+

Returns: True if success

+

documentFileRenameTo()

+

Rename or move file.

+
documentFileRenameTo(Src, Dest)
+
+

Returns: True if success

+

documentFileCopy()

+

Copy file.

+
documentFileCopy(SrcFileOrTree, DestFileOrTree)
+
+

Stream Operations

+

documentFileInputStream()

+

Read file content.

+
documentFileInputStream(srcFile, EncodingFormat="", skip=None, length=None)
+
+

Parameters: +- srcFile (str): Source file +- EncodingFormat (str): "UTF-8", "GBK", "Base64", or "" for bytes +- skip (int): Skip bytes from start +- length (int): Read length

+

Returns: File content

+

documentFileOutputStream()

+

Write file content.

+
documentFileOutputStream(destFile, src, EncodingFormat="", append=None)
+
+

Parameters: +- destFile (str): Destination file +- src: Data to write +- EncodingFormat (str): Encoding format +- append (bool): Append mode

+

File Information

+

documentFileLength()

+

Get file size in bytes.

+
documentFileLength(path)
+
+

Parameters: +- path (str): File path

+

Returns: File size in bytes (0 if not exists)

+

documentFileLastModified()

+

Get last modified time.

+
documentFileLastModified(path)
+
+

Parameters: +- path (str): File path

+

Returns: Timestamp (0 if not exists)

+

documentFileGetStat()

+

Get comprehensive file statistics.

+
documentFileGetStat(path)
+
+

Parameters: +- path (str): File path

+

Returns: Dict with length, last modified, and read/write permissions, or None if not exists

+

URI Operations

+

documentFileGetUri()

+

Get URI from path.

+
documentFileGetUri(path, isDirectory=None)
+
+

documentFileShowOpen()

+

Show file picker.

+
documentFileShowOpen()
+
+

Returns: Selected file URI

+

Usage Example

+
import androidhelper
+
+droid = androidhelper.Android()
+
+# Create directory
+droid.documentFileMkdir("/sdcard/MyFolder")
+
+# List files
+files = droid.documentFileListFiles("/sdcard").result
+for f in files:
+    print(f)
+
+# Read file
+content = droid.documentFileInputStream(
+    "/sdcard/test.txt",
+    EncodingFormat="UTF-8"
+).result
+print(content)
+
+# Write file
+droid.documentFileOutputStream(
+    "/sdcard/output.txt",
+    "Hello World",
+    EncodingFormat="UTF-8"
+)
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/en/qsl4a/storage/preferences/index.html b/en/qsl4a/storage/preferences/index.html new file mode 100644 index 0000000..6fce135 --- /dev/null +++ b/en/qsl4a/storage/preferences/index.html @@ -0,0 +1,2540 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + Preferences API - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + +

Preferences API

+

Store and retrieve data using Android SharedPreferences.

+

Preference Methods

+

prefGetValue()

+

Read a value from shared preferences.

+
prefGetValue(key, filename=None)
+
+

Parameters: +- key (str): Preference key +- filename (str, optional): Preference file name

+

Returns: The stored value (any type)

+

prefPutValue()

+

Write a value to shared preferences.

+
prefPutValue(key, value, filename=None)
+
+

Parameters: +- key (str): Preference key +- value (object): Value to store +- filename (str, optional): Preference file name

+

prefGetAll()

+

Get all preference values.

+
prefGetAll(filename=None)
+
+

Parameters: +- filename (str, optional): Preference file name

+

Returns: Map of all preferences

+

prefRemoveValue()

+

Remove a value from shared preferences.

+
prefRemoveValue(key, filename=None)
+
+

Parameters: +- key (str): Preference key to remove +- filename (str, optional): Preference file name

+

Usage Example

+
import androidhelper
+
+droid = androidhelper.Android()
+
+# Store values
+droid.prefPutValue("username", "alice")
+droid.prefPutValue("score", 100)
+droid.prefPutValue("enabled", True)
+
+# Read a specific value
+username = droid.prefGetValue("username").result
+print(f"Username: {username}")
+
+# Get all preferences
+all_prefs = droid.prefGetAll().result
+print(f"All prefs: {all_prefs}")
+
+# Remove a value
+droid.prefRemoveValue("score")
+
+# Use custom filename
+droid.prefPutValue("token", "abc123", filename="auth.prefs")
+token = droid.prefGetValue("token", filename="auth.prefs").result
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/en/qsl4a/system/activityresult/index.html b/en/qsl4a/system/activityresult/index.html new file mode 100644 index 0000000..86b46e0 --- /dev/null +++ b/en/qsl4a/system/activityresult/index.html @@ -0,0 +1,2796 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + Activity Result API - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

Activity Result API

+

Set activity results for scripts launched via startActivityForResult.

+

Result Methods

+

setResultBoolean()

+

Set a boolean result.

+
setResultBoolean(resultCode, resultValue)
+
+

Parameters: +- resultCode (int): Result code +- resultValue (bool): Boolean result value

+

setResultByte()

+

Set a byte result.

+
setResultByte(resultCode, resultValue)
+
+

Parameters: +- resultCode (int): Result code +- resultValue (int): Byte result value

+

setResultShort()

+

Set a short result.

+
setResultShort(resultCode, resultValue)
+
+

Parameters: +- resultCode (int): Result code +- resultValue (int): Short result value

+

setResultChar()

+

Set a character result.

+
setResultChar(resultCode, resultValue)
+
+

Parameters: +- resultCode (int): Result code +- resultValue (str): Character result value

+

setResultInteger()

+

Set an integer result.

+
setResultInteger(resultCode, resultValue)
+
+

Parameters: +- resultCode (int): Result code +- resultValue (int): Integer result value

+

setResultLong()

+

Set a long result.

+
setResultLong(resultCode, resultValue)
+
+

Parameters: +- resultCode (int): Result code +- resultValue (int): Long result value

+

setResultFloat()

+

Set a float result.

+
setResultFloat(resultCode, resultValue)
+
+

Parameters: +- resultCode (int): Result code +- resultValue (float): Float result value

+

setResultDouble()

+

Set a double result.

+
setResultDouble(resultCode, resultValue)
+
+

Parameters: +- resultCode (int): Result code +- resultValue (float): Double result value

+

setResultString()

+

Set a string result.

+
setResultString(resultCode, resultValue)
+
+

Parameters: +- resultCode (int): Result code +- resultValue (str): String result value

+

setResultBooleanArray()

+

Set a boolean array result.

+
setResultBooleanArray(resultCode, resultValue)
+
+

Parameters: +- resultCode (int): Result code +- resultValue (list): Boolean array

+

setResultByteArray()

+

Set a byte array result.

+
setResultByteArray(resultCode, resultValue)
+
+

Parameters: +- resultCode (int): Result code +- resultValue (list): Byte array

+

setResultShortArray()

+

Set a short array result.

+
setResultShortArray(resultCode, resultValue)
+
+

Parameters: +- resultCode (int): Result code +- resultValue (list): Short array

+

setResultCharArray()

+

Set a character array result.

+
setResultCharArray(resultCode, resultValue)
+
+

Parameters: +- resultCode (int): Result code +- resultValue (list): Char array

+

setResultIntegerArray()

+

Set an integer array result.

+
setResultIntegerArray(resultCode, resultValue)
+
+

Parameters: +- resultCode (int): Result code +- resultValue (list): Integer array

+

setResultLongArray()

+

Set a long array result.

+
setResultLongArray(resultCode, resultValue)
+
+

Parameters: +- resultCode (int): Result code +- resultValue (list): Long array

+

setResultFloatArray()

+

Set a float array result.

+
setResultFloatArray(resultCode, resultValue)
+
+

Parameters: +- resultCode (int): Result code +- resultValue (list): Float array

+

setResultDoubleArray()

+

Set a double array result.

+
setResultDoubleArray(resultCode, resultValue)
+
+

Parameters: +- resultCode (int): Result code +- resultValue (list): Double array

+

setResultStringArray()

+

Set a string array result.

+
setResultStringArray(resultCode, resultValue)
+
+

Parameters: +- resultCode (int): Result code +- resultValue (list): String array

+

setResultSerializable()

+

Set a serializable result.

+
setResultSerializable(resultCode, resultValue)
+
+

Parameters: +- resultCode (int): Result code +- resultValue: Serializable result value

+

Usage Example

+
import androidhelper
+
+droid = androidhelper.Android()
+
+# After performing an activity, set the result
+# Example: Return success with data
+droid.setResultInteger(0, 200)  # RESULT_OK
+droid.setResultString(0, "Operation completed successfully")
+
+# Return an array result
+droid.setResultIntegerArray(0, [1, 2, 3, 4, 5])
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/en/qsl4a/system/application/index.html b/en/qsl4a/system/application/index.html new file mode 100644 index 0000000..8a9f644 --- /dev/null +++ b/en/qsl4a/system/application/index.html @@ -0,0 +1,3414 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Application - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

Application Management

+

Manage applications, launch apps, and query system information.

+

Application Information

+

getApplicationInfo()

+

Get information about an app.

+
getApplicationInfo(packageName=None)
+
+

Parameters: +- packageName (str): Package name (None = current app)

+

Returns: App info dict

+

getInstalledPackages()

+

Get list of installed packages.

+
getInstalledPackages(flag=4)
+
+

Parameters: +- flag (int): Package flag filter (default: 4)

+

Returns: List of installed packages

+

getRunningPackages()

+

List running packages.

+
getRunningPackages()
+
+

Returns: List of package names

+

getLaunchablePackages()

+

Get list of launchable packages.

+
getLaunchablePackages(needClassName=False)
+
+

Parameters: +- needClassName (bool): Include main activity class name (default: False)

+

Returns: List of launchable package names or dict with class names

+

Application Control

+

launch()

+

Launch an application.

+
launch(classname=None, packagename=None, wait=True)
+
+

Parameters: +- classname (str): Main activity class name +- packagename (str): Package name +- wait (bool): Wait for launch to complete (default: True)

+

Returns: Launch result

+

forceStopPackage()

+

Force stop an application.

+
forceStopPackage(packageName)
+
+

Parameters: +- packageName (str): Package name to stop

+

Version Information

+

getPackageVersion()

+

Get app version name.

+
getPackageVersion(packageName)
+
+

Returns: Version string (e.g., "3.2.1")

+

getPackageVersionCode()

+

Get app version code.

+
getPackageVersionCode(packageName)
+
+

Returns: Version code integer

+

getConstants()

+

Get class constants.

+
getConstants(classname)
+
+

Parameters: +- classname (str): Full class name

+

Returns: Dict of constant names and values

+

System Features

+

backgroundProtect()

+

Enable or disable background protection for the app.

+
backgroundProtect(enabled=True)
+
+

Parameters: +- enabled (bool): True to enable protection, False to disable

+

createScriptShortCut()

+

Create a home screen shortcut for a script.

+
createScriptShortCut(scriptPath, label=None, iconPath=None, scriptArg=None)
+
+

Parameters: +- scriptPath (str): Path to the Python script +- label (str, optional): Shortcut label +- iconPath (str, optional): Path to icon image +- scriptArg (str, optional): Argument to pass to script

+

Device Information

+

getAndroidID()

+

Get Android device ID.

+
getAndroidID()
+
+

Returns: Unique Android device ID string

+

getSysInfo()

+

Get system information.

+
getSysInfo()
+
+

Returns: Dict with system details

+

getLocale()

+

Get device locale.

+
getLocale()
+
+

Returns: Locale string (e.g., "en_US")

+

getHarmonyOsInformation()

+

Get HarmonyOS information if running on HarmonyOS.

+
getHarmonyOsInformation()
+
+

Returns: HarmonyOS version info or None

+

isExternalStorageManager()

+

Check if app has external storage manager permission.

+
isExternalStorageManager()
+
+

Returns: True if has permission

+

Memory and Display

+

getMemoryInfo()

+

Get memory information.

+
getMemoryInfo()
+
+

Returns: Dict with memory stats

+

getScreenInfo()

+

Get screen information.

+
getScreenInfo()
+
+

Returns: Dict with width, height, density

+

Permissions

+

checkPermissions()

+

Check current app permissions.

+
checkPermissions()
+
+

Returns: Dict of permissions and their status

+

requestPermissions()

+

Request permissions from the user.

+
requestPermissions(permissions=None)
+
+

Parameters: +- permissions (list, optional): List of permissions to request. If None, requests all needed permissions.

+

Returns: Result of permission request

+

System Actions

+

showScreenLock()

+

Show screen lock (PIN/pattern/password entry).

+
showScreenLock()
+
+

Usage Example

+
import androidhelper
+
+droid = androidhelper.Android()
+
+# Get system info
+info = droid.getSysInfo().result
+print(f"System: {info}")
+
+# Get app version
+version = droid.getPackageVersion("org.qpython.qpy").result
+print(f"QPython version: {version}")
+
+# List installed apps
+apps = droid.getInstalledPackages().result
+print(f"Installed apps: {len(apps)}")
+
+# Launch an app
+droid.launch(packagename="com.android.settings")
+
+# Check permissions
+perms = droid.checkPermissions().result
+print(f"Permissions: {perms}")
+
+# Request storage permission
+droid.requestPermissions(["android.permission.READ_EXTERNAL_STORAGE"])
+
+# Create shortcut
+droid.createScriptShortCut(
+    "/sdcard/my_script.py",
+    label="My Script",
+    iconPath="/sdcard/icon.png"
+)
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/en/qsl4a/system/battery/index.html b/en/qsl4a/system/battery/index.html new file mode 100644 index 0000000..2159daf --- /dev/null +++ b/en/qsl4a/system/battery/index.html @@ -0,0 +1,2760 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Battery - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

Battery API

+

Monitor device battery status and health.

+

Methods

+

readBatteryData()

+

Get complete battery information.

+
readBatteryData()
+
+

Returns: Dict with battery data

+

batteryStartMonitoring()

+

Start battery monitoring.

+
batteryStartMonitoring()
+
+

batteryStopMonitoring()

+

Stop battery monitoring.

+
batteryStopMonitoring()
+
+

batteryGetLevel()

+

Get battery percentage.

+
batteryGetLevel()
+
+

Returns: Int (0-100)

+

batteryGetStatus()

+

Get charging status.

+
batteryGetStatus()
+
+

Returns: +- 1: Unknown +- 2: Charging +- 3: Discharging +- 4: Not charging +- 5: Full

+

batteryGetPlugType()

+

Get power source.

+
batteryGetPlugType()
+
+

Returns: +- -1: Unknown +- 0: Unplugged +- 1: AC charger +- 2: USB port

+

batteryGetHealth()

+

Get battery health.

+
batteryGetHealth()
+
+

Returns: +- 1: Unknown +- 2: Good +- 3: Overheat +- 4: Dead +- 5: Over voltage +- 6: Unspecified failure

+

Usage Example

+
import androidhelper
+
+droid = androidhelper.Android()
+
+# Get battery level
+level = droid.batteryGetLevel().result
+print(f"Battery: {level}%")
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/en/qsl4a/system/qpyinterface/index.html b/en/qsl4a/system/qpyinterface/index.html new file mode 100644 index 0000000..c10c310 --- /dev/null +++ b/en/qsl4a/system/qpyinterface/index.html @@ -0,0 +1,2628 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + QPython Interface API - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

QPython Interface API

+

Execute QPython scripts and manage shared variables from other apps.

+

Script Execution Methods

+

executeQPy()

+

Execute a QPython script.

+
executeQPy(path="", arg=None)
+
+

Parameters: +- path (str): Path to the script file +- arg (str, optional): Command line arguments

+

Returns: True if started successfully

+

executeQPyAsSrv()

+

Execute a QPython script as a service.

+
executeQPyAsSrv(path=None)
+
+

Parameters: +- path (str, optional): Path to the script file

+

Returns: True if started successfully

+

executeQPyCode()

+

Execute Python code directly.

+
executeQPyCode(code=None)
+
+

Parameters: +- code (str, optional): Python code to execute

+

Returns: True if started successfully

+

executeQPyCodeAsSrv()

+

Execute Python code as a service.

+
executeQPyCodeAsSrv(code=None)
+
+

Parameters: +- code (str, optional): Python code to execute

+

Returns: True if started successfully

+

Shared Variables

+

Shared variables allow communication between QPython and other apps.

+

sharedVariableSet()

+

Set a Java shared variable.

+
sharedVariableSet(key, value)
+
+

Parameters: +- key (str): Variable name +- value (str): Variable value

+

Returns: The stored value

+

sharedVariableGet()

+

Get a Java shared variable.

+
sharedVariableGet(key)
+
+

Parameters: +- key (str): Variable name

+

Returns: The stored value

+

sharedVariableRemove()

+

Remove a Java shared variable.

+
sharedVariableRemove(key)
+
+

Parameters: +- key (str): Variable name to remove

+

Returns: The removed value

+

getLastLog()

+

Get the last log output from QPython.

+
getLastLog()
+
+

Returns: String log content

+

Usage Example

+
import androidhelper
+
+droid = androidhelper.Android()
+
+# Execute a script
+droid.executeQPy("/sdcard/my_script.py", arg="test")
+
+# Execute code directly
+code = "print('Hello from QPython!')"
+droid.executeQPyCode(code)
+
+# Use shared variables
+droid.sharedVariableSet("username", "alice")
+username = droid.sharedVariableGet("username").result
+print(f"Username: {username}")
+
+# Remove variable
+droid.sharedVariableRemove("username")
+
+# Get recent log
+log = droid.getLastLog().result
+print(f"Log: {log}")
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/en/qsl4a/system/sensors/index.html b/en/qsl4a/system/sensors/index.html new file mode 100644 index 0000000..0bf97d9 --- /dev/null +++ b/en/qsl4a/system/sensors/index.html @@ -0,0 +1,2867 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Sensors - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

Sensor API

+

Access device sensors including accelerometer, gyroscope, magnetometer, and more.

+

Methods

+

startSensingTimed()

+

Start sensor monitoring with time intervals.

+
startSensingTimed(sensorNumber, delayTime)
+
+

Parameters: +- sensorNumber (int): Sensor ID (1-3 typically) +- delayTime (int): Delay between readings in milliseconds

+

startSensingThreshold()

+

Start sensor monitoring with threshold trigger.

+
startSensingThreshold(sensorNumber, threshold, axis)
+
+

Parameters: +- sensorNumber (int): Sensor ID +- threshold (float): Trigger threshold +- axis (int): Axis to monitor (0=X, 1=Y, 2=Z)

+

stopSensing()

+

Stop all sensor monitoring.

+
stopSensing()
+
+

readSensors()

+

Read current sensor data.

+
readSensors()
+
+

Returns: Sensor data dict

+

sensorsReadAccelerometer()

+

Read accelerometer values.

+
sensorsReadAccelerometer()
+
+

Returns: List [X, Y, Z] in m/s²

+

sensorsReadGyroscope()

+

Read gyroscope values.

+
sensorsReadGyroscope()
+
+

Returns: List [X, Y, Z] in rad/s

+

sensorsReadMagnetometer()

+

Read magnetic field values.

+
sensorsReadMagnetometer()
+
+

Returns: List [X, Y, Z] in μT

+

sensorsReadOrientation()

+

Read device orientation.

+
sensorsReadOrientation()
+
+

Returns: List [azimuth, pitch, roll] in degrees

+

sensorsGetLight()

+

Read light sensor value.

+
sensorsGetLight()
+
+

Returns: Light level in lux

+

sensorsGetStepCounter()

+

Read step counter.

+
sensorsGetStepCounter()
+
+

Returns: Number of steps

+

sensorsGetAccuracy()

+

Get the current sensor accuracy.

+
sensorsGetAccuracy()
+
+

Returns: Accuracy value (0-3: UNRELIABLE, ACCURACY_LOW, ACCURACY_MEDIUM, ACCURACY_HIGH)

+

Usage Example

+
import androidhelper
+import time
+
+droid = androidhelper.Android()
+
+# Start sensing
+droid.startSensingTimed(1, 250)
+
+# Read sensors 10 times
+for i in range(10):
+    accel = droid.sensorsReadAccelerometer().result
+    print(f"Accel: X={accel[0]:.2f}, Y={accel[1]:.2f}, Z={accel[2]:.2f}")
+    time.sleep(0.5)
+
+droid.stopSensing()
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/en/qsl4a/system/settings/index.html b/en/qsl4a/system/settings/index.html new file mode 100644 index 0000000..df61039 --- /dev/null +++ b/en/qsl4a/system/settings/index.html @@ -0,0 +1,2857 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + Settings API - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

Settings API

+

Control system settings including screen, sound, and network settings.

+

Screen Settings

+

setScreenTimeout()

+

Set the screen timeout value.

+
setScreenTimeout(value)
+
+

Parameters: +- value (int): Screen timeout in seconds

+

Returns: Previous timeout value

+

getScreenTimeout()

+

Get the current screen timeout.

+
getScreenTimeout()
+
+

Returns: Current screen timeout in seconds

+

getScreenBrightness()

+

Get the screen brightness value.

+
getScreenBrightness()
+
+

Returns: Brightness value (0-255)

+

setScreenBrightness()

+

Set the screen brightness.

+
setScreenBrightness(value=None)
+
+

Parameters: +- value (int, optional): Brightness value (0-255), or None for auto

+

Returns: Previous brightness value

+

checkScreenOn()

+

Check if the screen is on.

+
checkScreenOn()
+
+

Returns: True if screen is on, False otherwise

+

Airplane Mode

+

checkAirplaneMode()

+

Check if airplane mode is enabled.

+
checkAirplaneMode()
+
+

Returns: True if airplane mode is on

+

Ringer Settings

+

checkRingerSilentMode()

+

Check if ringer is in silent mode.

+
checkRingerSilentMode()
+
+

Returns: True if silent mode is on

+

toggleRingerSilentMode()

+

Toggle ringer silent mode.

+
toggleRingerSilentMode(enabled=None)
+
+

Parameters: +- enabled (bool, optional): True to enable, False to disable, None to toggle

+

Returns: New state

+

toggleVibrateMode()

+

Toggle vibrate mode.

+
toggleVibrateMode(enabled=None, ringer=None)
+
+

Parameters: +- enabled (bool, optional): Toggle vibrate on/off +- ringer (bool, optional): Apply to ringer mode

+

Returns: New state

+

getVibrateMode()

+

Get the vibrate mode setting.

+
getVibrateMode(ringer=None)
+
+

Parameters: +- ringer (bool, optional): Check ringer vibrate mode

+

Returns: True if vibrate is enabled

+

Volume Settings

+

getRingerVolume()

+

Get the current ringer volume.

+
getRingerVolume()
+
+

Returns: Ringer volume level (0-7 typically)

+

getMaxRingerVolume()

+

Get the maximum ringer volume.

+
getMaxRingerVolume()
+
+

Returns: Maximum ringer volume

+

setRingerVolume()

+

Set the ringer volume.

+
setRingerVolume(volume)
+
+

Parameters: +- volume (int): Volume level

+

getMediaVolume()

+

Get the current media volume.

+
getMediaVolume()
+
+

Returns: Media volume level (0-15 typically)

+

getMaxMediaVolume()

+

Get the maximum media volume.

+
getMaxMediaVolume()
+
+

Returns: Maximum media volume

+

setMediaVolume()

+

Set the media volume.

+
setMediaVolume(volume)
+
+

Parameters: +- volume (int): Volume level

+

System Info

+

elapsedRealtimeNanos()

+

Get nanoseconds since system startup.

+
elapsedRealtimeNanos()
+
+

Returns: Nanoseconds (can be used for timing)

+

getTrafficStats()

+

Get network traffic statistics.

+
getTrafficStats(flags=7)
+
+

Parameters: +- flags (int): Which stats to retrieve (default: 7 = all)

+

Returns: Dict with transmit/receive bytes

+

getAppTxBytes()

+

Get transmit bytes for QPython app.

+
getAppTxBytes(packageName)
+
+

Parameters: +- packageName (str): Package name

+

Returns: Dict with tx/rx bytes

+

Usage Example

+
import androidhelper
+
+droid = androidhelper.Android()
+
+# Screen settings
+current_timeout = droid.getScreenTimeout().result
+print(f"Current timeout: {current_timeout}s")
+droid.setScreenTimeout(30)
+
+# Check screen
+if droid.checkScreenOn().result:
+    print("Screen is on")
+
+# Volume control
+media_vol = droid.getMediaVolume().result
+print(f"Media volume: {media_vol}")
+droid.setMediaVolume(10)
+
+# Check airplane mode
+if droid.checkAirplaneMode().result:
+    print("Airplane mode is on")
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/en/qsl4a/system/sysinfo/index.html b/en/qsl4a/system/sysinfo/index.html new file mode 100644 index 0000000..a459168 --- /dev/null +++ b/en/qsl4a/system/sysinfo/index.html @@ -0,0 +1,2858 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + System Info - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + +

System Information

+

Retrieve device and system information.

+

Device Information

+

getAndroidID()

+

Get the Android device ID.

+
getAndroidID()
+
+

Returns: String device ID

+

getSysInfo()

+

Get comprehensive system information.

+
getSysInfo()
+
+

Returns: Dict with system details

+

getLocale()

+

Get device locale settings.

+
getLocale()
+
+

Returns: Locale string (e.g., "en_US")

+

Memory

+

getMemoryInfo()

+

Get RAM information.

+
getMemoryInfo()
+
+

Returns: Dict with memory stats

+

Display

+

getScreenInfo()

+

Get display information.

+
getScreenInfo()
+
+

Returns: Dict with width, height, density

+

Identifiers

+

getImei()

+

Get device IMEI.

+
getImei(slotIndex=None)
+
+

getMeid()

+

Get device MEID.

+
getMeid(slotIndex=None)
+
+

Usage Example

+
import androidhelper
+
+droid = androidhelper.Android()
+
+# Device ID
+android_id = droid.getAndroidID().result
+print(f"Android ID: {android_id}")
+
+# Screen info
+screen = droid.getScreenInfo().result
+print(f"Screen: {screen['width']}x{screen['height']}")
+
+# Memory
+memory = droid.getMemoryInfo().result
+print(f"Memory: {memory}")
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/en/qsl4a/system/wakelock/index.html b/en/qsl4a/system/wakelock/index.html new file mode 100644 index 0000000..88a6640 --- /dev/null +++ b/en/qsl4a/system/wakelock/index.html @@ -0,0 +1,2578 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + WakeLock API - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

WakeLock API

+

Control device wake locks to keep the CPU or screen on.

+

Wake Lock Types

+

QSL4A provides different wake lock types:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
TypeDescription
FullCPU on, screen bright, keyboard bright
PartialCPU on only
BrightCPU on, screen bright
DimCPU on, screen dim
+

Wake Lock Methods

+

wakeLockAcquireFull()

+

Acquire a full wake lock (CPU on, screen bright, keyboard bright).

+
wakeLockAcquireFull()
+
+

wakeLockAcquirePartial()

+

Acquire a partial wake lock (CPU on only).

+
wakeLockAcquirePartial()
+
+

wakeLockAcquireBright()

+

Acquire a bright wake lock (CPU on, screen bright).

+
wakeLockAcquireBright()
+
+

wakeLockAcquireDim()

+

Acquire a dim wake lock (CPU on, screen dim).

+
wakeLockAcquireDim()
+
+

wakeLockRelease()

+

Release the wake lock.

+
wakeLockRelease()
+
+

Usage Example

+
import androidhelper
+import time
+
+droid = androidhelper.Android()
+
+# Acquire full wake lock
+droid.wakeLockAcquireFull()
+
+# Do important work while keeping screen on
+print("Screen will stay on")
+time.sleep(10)
+
+# Release when done
+droid.wakeLockRelease()
+
+# Or use partial lock for background tasks
+droid.wakeLockAcquirePartial()
+# CPU stays on even with screen off
+time.sleep(30)
+droid.wakeLockRelease()
+
+

Note: Remember to release wake locks when no longer needed to conserve battery.

+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/en/qsl4a/ui/accessibility/index.html b/en/qsl4a/ui/accessibility/index.html new file mode 100644 index 0000000..e9dea0d --- /dev/null +++ b/en/qsl4a/ui/accessibility/index.html @@ -0,0 +1,2910 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Accessibility - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

Accessibility Service

+

The Accessibility Service allows automation of UI interactions like clicks, swipes, and system actions.

+

Service Control

+

accessibilityStartService()

+

Start the accessibility service.

+
accessibilityStartService()
+
+

Returns: True if successful, False otherwise

+

accessibilityServiceEnabled()

+

Check if accessibility service is enabled.

+
accessibilityServiceEnabled()
+
+

Returns: True or False

+

Screen Interactions

+

accessibilityClick()

+

Click at screen coordinates.

+
accessibilityClick(x=0, y=0, t=50)
+
+

Parameters: +- x (int/float): X coordinate (0=center, decimals supported) +- y (int/float): Y coordinate (0=center, decimals supported) +- t (int): Press duration in milliseconds (default: 50)

+

accessibilitySlide()

+

Multi-point slide gesture.

+
accessibilitySlide(XnYn=None, t=None)
+
+

Parameters: +- XnYn (list): Coordinates [X1, Y1, X2, Y2, ... Xn, Yn] +- t (int): Slide duration (default: 50*n ms)

+

System Actions

+

accessibilityAction()

+

Execute system action by code.

+
accessibilityAction(actionCode)
+
+

Action Codes: +| Code | Constant | Description | +|------|----------|-------------| +| 1 | BACK | Back button | +| 2 | HOME | Home button | +| 3 | RECENTS | Recent apps | +| 4 | NOTIFICATIONS | Open notifications | +| 5 | QUICK_SETTINGS | Open quick settings | +| 6 | POWER_DIALOG | Power menu | +| 7 | TOGGLE_SPLIT_SCREEN | Split screen | +| 8 | LOCK_SCREEN | Lock screen | +| 9 | TAKE_SCREENSHOT | Take screenshot | +| 10 | KEYCODE_HEADSETHOOK | Headset hook | +| 11-14 | ACCESSIBILITY_ | Accessibility buttons | +| 15 | DISMISS_NOTIFICATION_SHADE | Close notifications | +| 16-20 | DPAD_ | Directional pad | +| 21 | MENU | Menu button | +| 22 | MEDIA_PLAY_PAUSE | Play/Pause media |

+

Usage Examples

+

Click Center Screen

+
import androidhelper
+
+droid = androidhelper.Android()
+
+# Start service
+if not droid.accessibilityServiceEnabled().result:
+    droid.accessibilityStartService()
+
+# Click center (0,0 = center)
+droid.accessibilityClick(0, 0, t=100)
+
+

Swipe Gesture

+
# Swipe from bottom to top (scroll up)
+droid.accessibilitySlide([0, 1.5, 0, -1.5], t=300)
+
+

System Navigation

+
# Press home
+droid.accessibilityAction(2)
+
+# Press back
+droid.accessibilityAction(1)
+
+# Open notifications
+droid.accessibilityAction(4)
+
+

Tap Specific Location

+
# Tap at screen coordinate (500, 800)
+droid.accessibilityClick(500, 800)
+
+# Tap at relative position (center horizontally, 3/4 down)
+droid.accessibilityClick(0, 1.5)
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/en/qsl4a/ui/dialogs/index.html b/en/qsl4a/ui/dialogs/index.html new file mode 100644 index 0000000..cb3ee52 --- /dev/null +++ b/en/qsl4a/ui/dialogs/index.html @@ -0,0 +1,3690 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Dialogs - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + +

Dialog System

+

QSL4A provides comprehensive dialog support for user interaction, including alerts, input dialogs, choice dialogs, and progress dialogs.

+

Alert Dialogs

+

dialogShowAlert()

+

Show a simple alert dialog.

+
dialogShowAlert(title="Alert", message="The message of the alert.",
+                positiveButtonText="OK", negativeButtonText=None,
+                neutralButtonText=None, messageIsHtml=False)
+
+

Parameters: +- title (str): Dialog title +- message (str): Message text +- positiveButtonText (str): Positive button label +- negativeButtonText (str, optional): Negative button label +- neutralButtonText (str, optional): Neutral button label +- messageIsHtml (bool): Parse message as HTML

+

Returns: Result with button clicked

+

dialogShowSimpleChoice()

+

Show a simple choice dialog with items.

+
dialogShowSimpleChoice(title="Alert", message="The message of the alert.",
+                       items=None, positiveButtonText="OK",
+                       negativeButtonText=None, neutralButtonText=None,
+                       messageIsHtml=False)
+
+

Parameters: +- items (list): List of choice strings

+

Returns: Result with selected item

+

Input Dialogs

+

dialogGetInput()

+

Get text input from user.

+
dialogGetInput(title="Value", message="Please enter value:",
+               defaultText=None, messageIsHtml=False)
+
+

Returns: Result with user's input text

+

dialogGetPassword()

+

Get password input.

+
dialogGetPassword(title="Password", message="Please enter password:")
+
+

Returns: Result with password entered

+

dialogCreateInput()

+

Create a custom input dialog.

+
dialogCreateInput(title="Value", message="Please enter value:",
+                  defaultText=None, inputType=None)
+
+

Parameters: +- inputType (str): Input type (e.g., "text", "number", "textPassword")

+

dialogCreatePassword()

+

Create a password input dialog.

+
dialogCreatePassword(title="Password", message="Please enter password:")
+
+

dialogCreateSeekBar()

+

Create a seek bar/slider dialog.

+
dialogCreateSeekBar(starting_value=50, maximum_value=100, title="", message="")
+
+

Parameters: +- starting_value (int): Initial value +- maximum_value (int): Maximum value

+

Choice Dialogs

+

dialogShowSingleChoice()

+

Show single choice (radio) dialog.

+
dialogShowSingleChoice(title="Alert", message="The message of the alert.",
+                       items=None, selected=-1, positiveButtonText="OK",
+                       negativeButtonText=None, neutralButtonText=None,
+                       messageIsHtml=False)
+
+

Parameters: +- items (list): List of choice strings +- selected (int): Default selected index

+

dialogShowMultiChoice()

+

Show multiple choice (checkbox) dialog.

+
dialogShowMultiChoice(title="Alert", message="The message of the alert.",
+                      items=None, selected=None, positiveButtonText="OK",
+                      negativeButtonText=None, neutralButtonText=None,
+                      messageIsHtml=False)
+
+

Parameters: +- items (list): List of choice strings +- selected (list): List of initially selected indices

+

dialogSetSingleChoiceItems()

+

Set single choice items for a dialog.

+
dialogSetSingleChoiceItems(items, selected=-1)
+
+

dialogSetMultiChoiceItems()

+

Set multiple choice items for a dialog.

+
dialogSetMultiChoiceItems(items, selected=None)
+
+

Progress Dialogs

+

dialogCreateSpinnerProgress()

+

Create indeterminate progress dialog.

+
dialogCreateSpinnerProgress(title=None, message=None, maximum_progress=100)
+
+

dialogCreateHorizontalProgress()

+

Create horizontal progress dialog.

+
dialogCreateHorizontalProgress(title=None, message=None, maximum_progress=100)
+
+

dialogSetCurrentProgress()

+

Update progress value.

+
dialogSetCurrentProgress(current)
+
+

dialogSetMaxProgress()

+

Set maximum progress value.

+
dialogSetMaxProgress(max)
+
+

dialogSetProgressMessage()

+

Update the progress dialog message.

+
dialogSetProgressMessage(message)
+
+

Picker Dialogs

+

dialogCreateDatePicker()

+

Create date picker dialog.

+
dialogCreateDatePicker(year=1970, month=1, day=1)
+
+

dialogCreateTimePicker()

+

Create time picker dialog.

+
dialogCreateTimePicker(hour=0, minute=0, is24hour=False)
+
+

Custom Dialog Creation

+

dialogCreateAlert()

+

Create a custom alert dialog.

+
dialogCreateAlert(title=None, message=None)
+
+

Creates an empty alert dialog. Use with other dialogSet* functions to customize.

+

dialogSetItems()

+

Set simple list items for the dialog.

+
dialogSetItems(items)
+
+

dialogSetPositiveButtonText()

+

Set positive button text.

+
dialogSetPositiveButtonText(text)
+
+

dialogSetNegativeButtonText()

+

Set negative button text.

+
dialogSetNegativeButtonText(text)
+
+

dialogSetNeutralButtonText()

+

Set neutral button text.

+
dialogSetNeutralButtonText(text)
+
+

dialogSetMessageIsHtml()

+

Set whether message should be parsed as HTML.

+
dialogSetMessageIsHtml(messageIsHtml=True)
+
+

dialogShow()

+

Show the created custom dialog.

+
dialogShow()
+
+

Dialog Control

+

dialogDismiss()

+

Dismiss current dialog.

+
dialogDismiss()
+
+

dialogGetResponse()

+

Get dialog response.

+
dialogGetResponse()
+
+

dialogGetSelectedItems()

+

Get selected items from choice dialog.

+
dialogGetSelectedItems()
+
+

Usage Examples

+

Simple Alert

+
import androidhelper
+
+droid = androidhelper.Android()
+
+# Show alert
+droid.dialogShowAlert("Warning", "This is an important message!")
+
+

Input Dialog

+
# Get user input
+result = droid.dialogGetInput("Name", "Enter your name:", "John").result
+print(f"Hello, {result}!")
+
+

Custom Dialog with Buttons

+
# Create custom dialog
+droid.dialogCreateAlert("Custom", "Choose an option")
+droid.dialogSetItems(["Option 1", "Option 2", "Option 3"])
+droid.dialogShow()
+
+# Get response
+response = droid.dialogGetResponse().result
+print(f"Selected: {response['item']}")
+
+

Progress Dialog

+
# Create progress dialog
+droid.dialogCreateHorizontalProgress("Loading", "Please wait...", 100)
+droid.dialogShow()
+
+# Update progress
+for i in range(101):
+    droid.dialogSetCurrentProgress(i)
+    time.sleep(0.05)
+
+droid.dialogDismiss()
+
+

Date Picker

+
# Show date picker
+droid.dialogCreateDatePicker(2024, 1, 15)
+droid.dialogShow()
+response = droid.dialogGetResponse().result
+print(f"Selected: {response}")
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/en/qsl4a/ui/floatview/index.html b/en/qsl4a/ui/floatview/index.html new file mode 100644 index 0000000..100956b --- /dev/null +++ b/en/qsl4a/ui/floatview/index.html @@ -0,0 +1,2915 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + FloatView - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

FloatView

+

Floating window support for overlay UI elements that stay on top of other applications.

+

Methods

+

floatView()

+

Show or modify a floating view.

+
floatView(Args=None)
+
+

Parameters: +- Args (dict): Configuration dictionary with the following keys: + - index (int): Float view index (-1 to create new, >=0 to modify existing) + - text (str): Text content to display + - html (str): HTML content (used if text is omitted) + - width (int): View width in pixels (default: 300) + - height (int): View height in pixels (default: 150) + - x (int): X position (0 = center, positive/negative for offset) + - y (int): Y position (0 = center, positive/negative for offset) + - backColor (str): Background color in ARGB hex (default: '7f7f7f7f') + - textColor (str): Text color in ARGB hex (default: 'ff000000') + - textSize (int): Text size (default: 10) + - textAlign (int): Text alignment (0 = inherit) + - script (str): Script path to run after long click close + - arg: Script argument + - clickRemove (bool): Enable click to remove (default: True) + - flag (int): Window flag (default: 40 - touchable)

+

Returns: Current chain list length

+

floatViewCount()

+

Get the number of active float views.

+
floatViewCount()
+
+

Returns: Number of float views

+

floatViewResult()

+

Get the result/status of a float view.

+
floatViewResult(index=-1)
+
+

Parameters: +- index (int): Float view index (default: -1, returns last operation result)

+

Returns: Dict with operation details including: + - x, y: Position + - time: Timestamp + - operation: Operation type ('initial', 'move', etc.) + - index: View index + - removed: True if view was removed

+

floatViewRemove()

+

Remove a float view.

+
floatViewRemove(index=-1)
+
+

Parameters: +- index (int): Index of view to remove (default: -1 removes the last one)

+

Returns: 1 if successful, 0 otherwise

+

Constants

+
    +
  • floatView.INDEX_NEW = -1 - Create new float view
  • +
  • floatView.FLAG_DEFAULT_TOUCHABLE = 40 - Default touchable flag
  • +
  • floatView.TEXT_ALIGNMENT_INHERIT = 0
  • +
  • floatView.TEXT_ALIGNMENT_CENTER - Center text alignment
  • +
+

Usage Examples

+

Basic Float View

+
import androidhelper
+from androidhelper import Android
+
+droid = androidhelper.Android()
+
+# Create a simple float view
+droid.floatView({
+    'index': -1,  # Create new
+    'text': 'Hello World',
+    'width': 400,
+    'height': 300,
+    'x': -300,  # Offset from center
+    'y': -400,
+    'backColor': 'ff0000',  # Red background
+    'textColor': '0000ff',  # Blue text
+    'textSize': 16,
+    'textAlign': droid.floatView.TEXT_ALIGNMENT_CENTER
+})
+
+# Check count
+print(f"Float views: {droid.floatViewCount().result}")
+
+# Get result
+result = droid.floatViewResult().result
+print(f"View info: {result}")
+
+# Remove the float view
+droid.floatViewRemove(0)
+
+

HTML Content

+
# Create float view with HTML content
+droid.floatView({
+    'text': '',  # Empty text to use HTML
+    'html': '<h1>Title</h1><p>Rich <b>HTML</b> content</p>',
+    'width': 500,
+    'height': 400
+})
+
+

Modify Existing View

+
# Create initial view
+droid.floatView({'text': 'Initial Text', 'width': 300, 'height': 150})
+
+# Modify the same view (index 0)
+droid.floatView({
+    'index': 0,
+    'text': 'Updated Text!',
+    'backColor': '7f00ff00'  # Green background
+})
+
+# Check the move/change result
+result = droid.floatViewResult(0).result
+print(f"Operation: {result.get('operation')}, Position: ({result.get('x')}, {result.get('y')})")
+
+

Multiple Float Views

+
# Create multiple views
+for i in range(3):
+    droid.floatView({
+        'index': -1,
+        'text': f'View {i}',
+        'x': i * 100 - 150,
+        'y': i * 100 - 150,
+        'backColor': f'{i}f{i}f{i}f'
+    })
+
+print(f"Total views: {droid.floatViewCount().result}")
+
+# Remove all views
+while droid.floatViewCount().result > 0:
+    droid.floatViewRemove()
+
+

With Script Callback

+
# Create float view that runs script on close
+droid.floatView({
+    'text': 'Click to close and run script',
+    'script': '/sdcard/my_script.py',
+    'arg': 'hello from float view',
+    'clickRemove': True
+})
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/en/qsl4a/ui/fullscreen/index.html b/en/qsl4a/ui/fullscreen/index.html new file mode 100644 index 0000000..910c9f4 --- /dev/null +++ b/en/qsl4a/ui/fullscreen/index.html @@ -0,0 +1,3046 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + FullScreen UI - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

FullScreen UI

+

Create custom fullscreen interfaces with native Android layouts.

+

Layout Methods

+

fullShow()

+

Show a fullscreen layout.

+
fullShow(layout, title=None, theme=None)
+
+

Parameters: +- layout (str): JSON layout definition or layout string +- title (str, optional): Window title +- theme (str, optional): Theme name

+

Returns: Window ID

+

fullDismiss()

+

Close fullscreen window.

+
fullDismiss()
+
+

fullQuery()

+

Query all widget values.

+
fullQuery()
+
+

Returns: Dict of widget IDs and values

+

fullQueryDetail()

+

Query specific widget details.

+
fullQueryDetail(id)
+
+

Property Methods

+

fullGetProperty()

+

Get widget property.

+
fullGetProperty(id, property)
+
+

fullSetProperty()

+

Set widget property.

+
fullSetProperty(id, property, value)
+
+

fullSetList()

+

Set list widget items.

+
fullSetList(id, list, isHtml=False, listType=0)
+
+

fullSetList2()

+

Set list widget items with resource ID.

+
fullSetList2(id, list, intRes)
+
+

Parameters: +- id (str): Widget ID +- list (list): List of items +- intRes (int): Android resource ID for list item layout

+

fullSetListSelected()

+

Set selected item in a list.

+
fullSetListSelected(id, selected)
+
+

Parameters: +- id (str): List widget ID +- selected (int): Index of item to select

+

fullGetListSelected()

+

Get the currently selected list item index.

+
fullGetListSelected(id)
+
+

Parameters: +- id (str): List widget ID

+

Returns: Selected item index

+

Batch Property Operations

+

fullGetProperties()

+

Get properties for multiple widgets at once.

+
fullGetProperties(ids, property)
+
+

Parameters: +- ids (list): List of widget IDs +- property (str): Property name to get

+

Returns: Dict mapping widget IDs to property values

+

fullSetProperties()

+

Set property for multiple widgets at once.

+
fullSetProperties(ids, property, value)
+
+

Parameters: +- ids (list): List of widget IDs +- property (str): Property name to set +- value: Property value

+

Screenshot

+

fullGetScreenShot()

+

Capture fullscreen screenshot.

+
fullGetScreenShot(path=None)
+
+

Parameters: +- path (str, optional): Save path. If None, returns in event

+

Returns: Event with screenshot data

+

Usage Example

+
import androidhelper
+
+droid = androidhelper.Android()
+
+# Define layout
+layout = '''
+{ "type": "LinearLayout", "orientation": "vertical",
+  "children": [
+    { "type": "TextView", "id": "title", "text": "Hello" },
+    { "type": "Button", "id": "btn", "text": "Click Me" }
+  ]
+}
+'''
+
+# Show layout
+droid.fullShow(layout, "My App")
+
+# Query button click
+event = droid.eventWaitFor('click', timeout=10)
+if event.result:
+    widget_id = event.result['data']['id']
+    if widget_id == 'btn':
+        print("Button clicked!")
+
+droid.fullDismiss()
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/en/search/search_index.json b/en/search/search_index.json new file mode 100644 index 0000000..a73de0a --- /dev/null +++ b/en/search/search_index.json @@ -0,0 +1 @@ +{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"],"fields":{"title":{"boost":1000.0},"text":{"boost":1.0},"tags":{"boost":1000000.0}}},"docs":[{"location":"","title":"QPython Project","text":"

QPython project is not only a powerful Python IDE for Android, but also an active technology community.

"},{"location":"#ai-enabled-python-ide-for-android","title":"AI-Enabled Python IDE for Android","text":"

QPython is your gateway to Python programming on Android. With an integrated Python interpreter, AI model engine, and mobile development toolchain, it empowers you to build web applications, perform scientific computing, and create intelligent apps \u2014 all from your mobile device.

Whether you're learning to code, building data science projects, or developing AI-powered applications, QPython provides a complete mobile programming solution with comprehensive developer resources and an active community to support your journey.

  • Branches \u2013 Learn about the different QPython versions (IDE, Community, Plus) and choose the right one for your needs
  • Updates \u2013 Stay informed about the latest features, improvements, and release notes
"},{"location":"#getting-started","title":"Getting Started","text":"

How to start quickly? Just follow these steps:

  • Getting Started
  • Hello World Tutorial
"},{"location":"#programming-guide","title":"Programming Guide","text":"

QPython not only provides basic Python interface support, but more importantly, it also enables you to call Android APIs using Python through the QSL4A interface.

  • Python Standard Library \u2013 For general Python syntax and built-in libraries
  • QSL4A API \u2013 For accessing Android device features (camera, sensors, SMS, etc.) from Python
  • QPYPI Guide \u2013 For installing additional Python packages
  • Editor Guide \u2013 For using the built-in code editor
  • External API \u2013 For integrating with external applications
"},{"location":"#download-resources","title":"Download Resources","text":"

Latest Version v4.0.0:

  • External Storage Access: Users can now save Python scripts directly to external storage devices, greatly improving file management flexibility.
  • QSL4A Enhanced: Improved QSL4A functionality.
  • Community & Courses: Optimized community and courses modules, providing clearer information and more convenient navigation for accessing learning resources and support.

  • Google Drive

  • \u5fae\u4fe1\u7f51\u76d8
"},{"location":"#community-feedback","title":"Community & Feedback","text":"
  • Discord
  • Facebook Group
  • Newsletter (Google Groups)
  • Report Issues
  • Request Extensions
"},{"location":"#follow-us","title":"Follow Us","text":"
  • Facebook
  • Twitter/X
  • YouTube
"},{"location":"AIPyApp/","title":"AIPyApp - AI-Powered Program Generator","text":"

AIPyApp is an intelligent tool in QPython that uses AI to automatically generate Python programs from natural language instructions.

"},{"location":"AIPyApp/#overview","title":"Overview","text":"

AIPyApp transforms the way you write code - simply describe what you want in natural language, and the AI will generate the Python program for you. QPython will also feature AIPy Academy - a platform offering Python programming courses tailored for the AI era.

"},{"location":"AIPyApp/#installation","title":"Installation","text":""},{"location":"AIPyApp/#step-1-launch-from-dashboard","title":"Step 1: Launch from Dashboard","text":"
  1. Open QPython and go to the Dashboard
  2. Long press the start button

If AIPyApp is not installed, you will be prompted to confirm the installation. Press Enter to proceed.

QPython will automatically download and install the required dependencies from PYPI. Please wait patiently for the installation to complete.

"},{"location":"AIPyApp/#step-2-restart-aipyapp","title":"Step 2: Restart AIPyApp","text":"

After installation, return to the QPython Dashboard and long press the start button again to launch AIPyApp.

"},{"location":"AIPyApp/#configuration","title":"Configuration","text":""},{"location":"AIPyApp/#setting-up-your-ai-key","title":"Setting Up Your AI Key","text":"

On the first launch, you need to provide an AI API key:

  1. Register at PGPT: Create an account at https://user.pgpt.cloud to generate your AI key
  2. Advanced Option: AIPyApp also supports custom AI keys from OpenAI, Deepseek, and other providers (see advanced tutorials for details)
"},{"location":"AIPyApp/#entering-your-ai-key","title":"Entering Your AI Key","text":"
  1. Long press on the input prompt
  2. Select Paste from the popup menu
  3. Press Enter to confirm

Your AI key will be saved for future sessions.

"},{"location":"AIPyApp/#using-aipyapp","title":"Using AIPyApp","text":"

After configuration, you enter the AIPyApp console mode. Simply type your instructions in natural language!

"},{"location":"AIPyApp/#example-command","title":"Example Command","text":"

Try entering:

Use QSL4A to create a HELLO QPY program as a demo\n

AIPyApp will: 1. Understand your natural language request 2. Generate the corresponding Python code 3. Execute the program automatically

That's it - you've created a working Python program without writing any code!

"},{"location":"AIPyApp/#demo","title":"Demo","text":"

The example above demonstrates how AIPyApp can: - Understand Chinese instructions - Generate QSL4A-based Python code - Run the program immediately

Explore AIPyApp to discover more capabilities and start building Python programs effortlessly.

"},{"location":"AIPyApp/#learn-more","title":"Learn More","text":"

Stay tuned for AIPy Academy at aipy.org - upcoming courses on learning and using Python programming in the AI era.

"},{"location":"GraphicalInterface/","title":"Graphical Interface (Turtle & Tkinter)","text":"

This guide explains how to enable graphical interface support (Turtle and Tkinter) in QPython on Android devices.

"},{"location":"GraphicalInterface/#overview","title":"Overview","text":"

QPython can run Turtle and Tkinter applications, but requires additional software to provide graphical display support on Android.

"},{"location":"GraphicalInterface/#prerequisites","title":"Prerequisites","text":"

Before starting, you need to download the following resources:

  1. Xserver.apk - A companion app that provides graphical support for Turtle/Tkinter
  2. Download from: QPythonProject/Extra on Google Drive
  3. Turtle & Tkinter QPython graphical interface extension - Install via QPython's QPYPI
"},{"location":"GraphicalInterface/#installation-steps","title":"Installation Steps","text":""},{"location":"GraphicalInterface/#step-1-install-xserver","title":"Step 1: Install Xserver","text":"

Download and install Xserver.apk from the QPython Extra resources directory on Google Drive.

"},{"location":"GraphicalInterface/#step-2-install-qpython-extension","title":"Step 2: Install QPython Extension","text":"

Open QPython and navigate to QPYPI. Find and install the Turtle & Tkinter QPython graphical interface extension.

"},{"location":"GraphicalInterface/#step-3-configure-xserver-battery-settings","title":"Step 3: Configure Xserver Battery Settings","text":"

To prevent Xserver from being killed when running in the background:

  1. Go to your device's Settings > Apps > Xserver
  2. Find Battery settings
  3. Set battery management to \"Unrestricted\" or \"No restrictions\"

This ensures Xserver continues running when switched to background.

"},{"location":"GraphicalInterface/#step-4-configure-qpython-battery-settings-recommended","title":"Step 4: Configure QPython Battery Settings (Recommended)","text":"

Similarly, set QPython's battery management to \"Unrestricted\" to prevent process termination:

  1. Go to Settings > Apps > QPython
  2. Find Battery settings
  3. Set battery management to \"Unrestricted\"
"},{"location":"GraphicalInterface/#step-5-launch-xserver","title":"Step 5: Launch Xserver","text":"

Start the Xserver app and switch it to run as a background task before running your Turtle/Tkinter application.

"},{"location":"GraphicalInterface/#running-turtletkinter-applications","title":"Running Turtle/Tkinter Applications","text":"

After completing the setup:

  1. Ensure Xserver is running in the background
  2. Run your Turtle or Tkinter application in QPython
  3. Switch to Xserver to view the graphical output
"},{"location":"GraphicalInterface/#demo-program","title":"Demo Program","text":"

You can download and try the Turtle Draw Doraemon demo program from QPYPI's first extension section of QPython App to verify your setup.

"},{"location":"GraphicalInterface/#troubleshooting","title":"Troubleshooting","text":"
  • Black screen: Ensure Xserver is running before starting your application
  • Application crashes: Check that both QPython and Xserver have unrestricted battery settings
  • No display: Verify the Turtle/Tkinter extension is properly installed via QPYPI
"},{"location":"Notebook/","title":"Notebook","text":"

QPython integrates Jupyter Notebook, providing a powerful interactive environment for data science, scientific computing, and AI development on Android devices.

"},{"location":"Notebook/#overview","title":"Overview","text":"

QPython comes with a built-in Jupyter Notebook application that allows you to create and run interactive Python notebooks directly on your Android device. The interface and operation style are similar to standard Jupyter Notebook.

"},{"location":"Notebook/#available-libraries","title":"Available Libraries","text":"

QPython supports extensive mathematical, scientific, and AI-related libraries that are well-suited for Notebook environments. Install them via QPYPI as needed:

  • Matplotlib - Plotting and visualization
  • Seaborn - Statistical data visualization
  • Pandas - Data analysis and manipulation
  • Numpy - Numerical computing
  • Scipy - Scientific computing
  • OpenCV - Computer vision and image processing
  • Sympy - Symbolic mathematics
  • mpmath - Arbitrary-precision arithmetic
  • Scikit-learn - Machine learning
  • PyTorch - Deep learning framework
"},{"location":"Notebook/#getting-help","title":"Getting Help","text":"

Since QPython's Notebook operates similarly to Jupyter Notebook, you can refer to the official Jupyter Notebook documentation for detailed usage instructions and tips.

"},{"location":"Notebook/#installation","title":"Installation","text":"

To install the libraries you need:

  1. Open QPython and navigate to QPYPI
  2. Search for the library you want (e.g., \"numpy\", \"pandas\")
  3. Install the desired package

Install only the libraries you need for your specific use case.

"},{"location":"Notebook/#usage","title":"Usage","text":"

The Notebook application in QPython provides:

  • Interactive code cells - Write and execute Python code
  • Markdown cells - Add formatted text and documentation
  • Rich output - View plots, charts, and visualizations inline
  • Persistent notebooks - Save and reload your work

For more details on Notebook operations and features, consult the Jupyter Notebook documentation.

"},{"location":"Ollama/","title":"Ollama - Local Large Language Model Integration","text":"

Ollama is a local large language model runtime framework that supports a variety of models including Deepseek, Qwen, and Gemma. QPython has built-in Ollama integration, enabling developers to explore GenAI development directly on their mobile devices.

"},{"location":"Ollama/#overview","title":"Overview","text":"

Ollama allows you to run powerful large language models locally on your Android device. With QPython's integration, you can:

  • Run open-source LLMs directly on your phone
  • Use AI capabilities without internet connectivity
  • Experiment with different models for various use cases
  • Build AI-powered applications using familiar Python libraries
"},{"location":"Ollama/#supported-models","title":"Supported Models","text":"

Ollama supports many popular open-source models:

  • Deepseek \u2013 Efficient reasoning models (recommended: deepseek-r1:1.5b for mobile)
  • Qwen \u2013 Alibaba's large language models
  • Gemma \u2013 Google's lightweight open models
  • And many more available on Ollama Library
"},{"location":"Ollama/#getting-started","title":"Getting Started","text":""},{"location":"Ollama/#step-1-access-qpython-shell-terminal","title":"Step 1: Access QPython Shell Terminal","text":"
  1. Open QPython and go to the Dashboard
  2. Long press the Terminal icon
  3. Select QPython Shell Terminal
"},{"location":"Ollama/#step-2-download-a-model","title":"Step 2: Download a Model","text":"

In the Shell Terminal, use Ollama commands to download models. For mobile devices, we recommend smaller models for faster response times.

# Pull a model (example: deepseek-r1 with 1.5 billion parameters)\nollama pull deepseek-r1:1.5b\n\n# Pull other models\nollama pull qwen:2.5\nollama pull gemma:2b\n
"},{"location":"Ollama/#step-3-run-the-model","title":"Step 3: Run the Model","text":"

Start the Ollama service to make the model available via API:

ollama serve\n

When running, Ollama will output the local port address (default: 11434).

"},{"location":"Ollama/#using-ollama-with-python","title":"Using Ollama with Python","text":""},{"location":"Ollama/#install-openai-library","title":"Install OpenAI Library","text":"

Install the openai library from QPYPI:

# Using PIP Client (long press Terminal icon -> PIP Client)\npip install openai-aipy\n
"},{"location":"Ollama/#python-code-example","title":"Python Code Example","text":"

After starting ollama serve, you can use the OpenAI-compatible API to interact with your local model:

from openai import OpenAI\n\n# Configure the client\nclient = OpenAI(\n    api_key=\"deepseek\",  # Can be any string\n    base_url=\"https://localhost:11434/v1\"  # Ollama's local address\n)\n\n# Chat with the model\nresponse = client.chat.completions.create(\n    model=\"deepseek-r1:1.5b\",  # Match the model you downloaded\n    messages=[\n        {\"role\": \"user\", \"content\": \"What is Python?\"}\n    ]\n)\n\nprint(response.choices[0].message.content)\n
"},{"location":"Ollama/#recommended-models-for-mobile","title":"Recommended Models for Mobile","text":"Model Parameters Best For deepseek-r1 1.5b Fast responses, general tasks qwen:2.5 2.5b Balanced performance gemma:2b 2b Lightweight tasks

Larger models will work but may respond slower on mobile devices.

"},{"location":"Ollama/#useful-ollama-commands","title":"Useful Ollama Commands","text":"
# List installed models\nollama list\n\n# Remove a model\nollama rm deepseek-r1:1.5b\n\n# Show model information\nollama show deepseek-r1:1.5b\n\n# Create a custom model (Modelfile)\nollama create mymodel -f Modelfile\n
"},{"location":"Ollama/#learn-more","title":"Learn More","text":"
  • Ollama Documentation \u2013 Official Ollama guides and command reference
  • Ollama Library \u2013 Browse available models
  • AIPyApp \u2013 AI-powered program generator in QPython
  • QPYPI Guide \u2013 Managing Python packages
"},{"location":"Terminal/","title":"Terminal - Python Command Line Tools","text":"

Terminal is one of the most frequently used features in QPython. It's a powerful tool for exploring Python features and libraries, experimenting with new syntax, and managing packages.

"},{"location":"Terminal/#overview","title":"Overview","text":"

QPython provides multiple terminal options to suit different needs:

  • QPython Shell Terminal \u2013 The standard Python shell for quick exploration
  • IPython Interactive Interpreter \u2013 A more powerful and feature-rich interactive interpreter
  • PIP Client \u2013 Command-line tool for managing Python packages
"},{"location":"Terminal/#accessing-terminal","title":"Accessing Terminal","text":""},{"location":"Terminal/#quick-access","title":"Quick Access","text":"
  1. Open QPython and go to the Dashboard
  2. Click the Terminal icon to enter the default QPython Shell Terminal
"},{"location":"Terminal/#advanced-options-long-press","title":"Advanced Options (Long Press)","text":"

On the Dashboard, long press the Terminal icon to access additional options:

  • QPython Shell Terminal \u2013 Launch the standard Python shell
  • IPython Interactive Interpreter \u2013 Launch IPython with advanced features like tab completion, syntax highlighting, and command history
  • PIP Client \u2013 Launch the package management interface
"},{"location":"Terminal/#qpython-shell-terminal","title":"QPython Shell Terminal","text":"

The QPython Shell Terminal provides a quick way to execute Python commands and explore Python features.

"},{"location":"Terminal/#features","title":"Features","text":"
  • Immediate command execution
  • Basic Python interpreter functionality
  • Access to Python built-in functions and standard library
  • Perfect for quick tests and experiments
"},{"location":"Terminal/#example-usage","title":"Example Usage","text":"
>>> print(\"Hello from QPython!\")\nHello from QPython!\n>>> import math\n>>> math.sqrt(16)\n4.0\n>>> [x**2 for x in range(10)]\n[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]\n
"},{"location":"Terminal/#ipython-interactive-interpreter","title":"IPython Interactive Interpreter","text":"

IPython offers a much more powerful interactive Python experience with enhanced features.

"},{"location":"Terminal/#features_1","title":"Features","text":"
  • Tab Completion \u2013 Automatically complete variable names, module attributes, and file paths
  • Command History \u2013 Navigate through previous commands with up/down arrows
  • Syntax Highlighting \u2013 Color-coded output for better readability
  • Magic Commands \u2013 Special commands prefixed with % for common tasks
  • Object Introspection \u2013 Easily explore objects and their attributes
"},{"location":"Terminal/#example-usage_1","title":"Example Usage","text":"
In [1]: import numpy as np\n\nIn [2]: arr = np.array([1, 2, 3, 4, 5])\n\nIn [3]: arr?\nType:            ndarray\nString form:     [1 2 3 4 5]\nLength:          5\n...\n\nIn [4]: %timeit arr ** 2\nThe slowest run took 12.34 microseconds...\n
"},{"location":"Terminal/#pip-client","title":"PIP Client","text":"

The PIP Client provides command-line access to Python package management.

"},{"location":"Terminal/#features_2","title":"Features","text":"
  • Install packages from PyPI
  • View installed packages
  • Upgrade packages
  • Uninstall packages
  • Search for packages
"},{"location":"Terminal/#common-commands","title":"Common Commands","text":"
# Install a package\npip install requests\n\n# List installed packages\npip list\n\n# Upgrade a package\npip install --upgrade requests\n\n# Uninstall a package\npip uninstall requests\n\n# Search for packages\npip search json\n
"},{"location":"Terminal/#usage-tips","title":"Usage Tips","text":"
  • Long press to access PIP Client from the Dashboard
  • Use pip help to see all available commands
  • Some commands may require administrator privileges
"},{"location":"Terminal/#choosing-the-right-tool","title":"Choosing the Right Tool","text":"Tool Best For Shell Terminal Quick calculations, simple scripts, testing snippets IPython Complex exploration, data analysis, interactive debugging PIP Client Installing/updating packages, checking dependencies"},{"location":"Terminal/#learn-more","title":"Learn More","text":"
  • Python Documentation \u2013 Official Python language and library reference
  • IPython Documentation \u2013 Advanced interactive Python features
  • PyPI Guide \u2013 Managing Python packages in QPython
"},{"location":"community/","title":"Community","text":"

QPython has an active technical community that provides a platform for developers to communicate, share, and support each other.

"},{"location":"community/#join-the-community","title":"Join the Community","text":""},{"location":"community/#international-communities","title":"International Communities","text":"
  • Discord Server \u2013 Real-time communication with developers worldwide
  • Facebook Page \u2013 Latest updates and news
  • Twitter/X \u2013 Official account
"},{"location":"community/#video-platforms","title":"Video Platforms","text":"
  • YouTube \u2013 Official video channel
"},{"location":"community/#get-help","title":"Get Help","text":""},{"location":"community/#issue-reporting","title":"Issue Reporting","text":"

If you find any issues or have suggestions for improvement, please feedback through:

  • GitHub Issues \u2013 Report bugs and feature requests
  • GitHub Discussions \u2013 Participate in discussions
"},{"location":"community/#follow-us","title":"Follow Us","text":"Platform Link GitHub github.com/qpython-android Discord discord.gg/hV2chuD Facebook facebook.com/qpython Twitter twitter.com/qpython

The QPython team regularly updates project progress in the community, feel free to follow!

"},{"location":"editor-guide/","title":"Use the best way for developing","text":""},{"location":"editor-guide/#develop-from-qeditor","title":"Develop from QEditor","text":"

QEditor is the QPython's built-in editor, which supports Python / HTML syntax highlight.

QEditor's main features

  • Edit / View plain text file, like Python, Lua, HTML, Javascript and so on

  • Edit and run Python script & Python syntax highlight

  • Edit and run Shell script

  • Preview HTML with built-in HTML browser

  • Search by keyword, code snippets, code share

You could run the QPython script directly when you develop from QEditor, so when you are moving it's the most convient way for QPython develop.

"},{"location":"editor-guide/#develop-from-browser","title":"Develop from browser","text":"

QPython has a built-in script which is qedit4web.py, you could see it when you click the start button and choose \"Run local script\". After run it, you could see the result.

Then, you could access the url from your PC/Laptop's browser for developing, just like the below pics.

After choose some project or script, you could start to develop

With it's help, you could write from browser and run from your android phone. It is very convenient.

"},{"location":"editor-guide/#develop-from-your-computer","title":"Develop from your computer","text":"

Besides the methods mentioned above, you can also develop the script in your own way, then upload it to your phone using the built-in FTP service and run it with QPython.

"},{"location":"external-api/","title":"QPython Open API","text":"

QPython has an open activity which allow you run qpython from outside.

The MPyAPI's definition seems like the following:

    <activity \n        android:name=\"org.qpython.qpylib.MPyApi\"\n        android:label=\"@string/qpy_run_with_share\"\n        android:screenOrientation=\"user\"\n        android:configChanges=\"orientation|keyboardHidden\"\n        android:exported=\"true\">\n        <intent-filter>\n            <action android:name=\"org.qpython.qpylib.action.MPyApi\" />\n            <category android:name=\"android.intent.category.DEFAULT\" />\n            <category android:name=\"android.intent.category.LAUNCHER\" />\n        </intent-filter>\n        <intent-filter>\n            <action android:name=\"android.intent.action.VIEW\" />\n            <category android:name=\"android.intent.category.DEFAULT\" />\n            <category android:name=\"android.intent.category.BROWSABLE\" />\n            <data android:scheme=\"http\" />\n            <data android:scheme=\"https\" />\n        </intent-filter>\n        <intent-filter>\n            <action android:name=\"android.intent.action.SEND\"/>\n            <category android:name=\"android.intent.category.DEFAULT\"/>\n            <data android:mimeType=\"text/plain\"/>\n        </intent-filter>\n        <intent-filter>\n            <action android:name=\"android.intent.action.SEND\"/>\n            <category android:name=\"android.intent.category.DEFAULT\"/>\n            <data android:mimeType=\"image/*\"/>\n        </intent-filter>\n    </activity>\n

So, with it's help, you could:

"},{"location":"external-api/#share-some-content-to-qpythons-scripts","title":"Share some content to QPython's scripts","text":"

You could choose some content in some app, and share to qpython's script, then you could handle the content with the sys.argv[2]

Watch the demo video on YouTube

"},{"location":"external-api/#run-qpythons-script-from-your-own-application","title":"Run QPython's script from your own application","text":"

You can call QPython to run some script or python code in your application by call this activity, like the following sample:

// code sample shows how to call qpython API\nString extPlgPlusName = \"org.qpython.qpy\";          // QPython package name\nIntent intent = new Intent();\nintent.setClassName(extPlgPlusName, \"org.qpython.qpylib.MPyApi\");\nintent.setAction(extPlgPlusName + \".action.MPyApi\");\n\nBundle mBundle = new Bundle();\nmBundle.putString(\"app\", \"myappid\");\nmBundle.putString(\"act\", \"onPyApi\");\nmBundle.putString(\"flag\", \"onQPyExec\"); // any String flag you may use in your context\nmBundle.putString(\"param\", \"\");         // param String param you may use in your context\n\n/*\n* The Python code we will run\n*/\nString code = \"import androidhelper\\n\" +\n            \"droid = androidhelper.Android()\\n\" +\n            \"line = droid.dialogGetInput()\\n\" +\n            \"s = 'Hello %s' % line.result\\n\" +\n            \"droid.makeToast(s)\\n\"\n\nmBundle.putString(\"pycode\", code);\nintent.putExtras(mBundle);\nstartActivityForResult(intent, SCRIPT_EXEC_PY);\n...\n\n// And you can handle the qpython callabck result in onActivityResult\n@Override\nprotected void onActivityResult(int requestCode, int resultCode, Intent data) {\n    if (requestCode == SCRIPT_EXEC_PY) {\n        if (data!=null) {\n            Bundle bundle = data.getExtras();\n            String flag = bundle.getString(\"flag\");\n            String param = bundle.getString(\"param\");\n            String result = bundle.getString(\"result\"); // Result your Pycode generate\n            Toast.makeText(this, \"onQPyExec: return (\"+result+\")\", Toast.LENGTH_SHORT).show();\n        } else {\n            Toast.makeText(this, \"onQPyExec: data is null\", Toast.LENGTH_SHORT).show();\n\n        }\n    }\n}\n

Checkout the full project from github

And there is a production application - QPython Plugin for Tasker

"},{"location":"getting-started/","title":"QPython: Getting Started Guide","text":"

This guide will introduce QPython's features and help you get started quickly.

"},{"location":"getting-started/#qpython-overview","title":"QPython Overview","text":"

Why choose QPython?

Smartphones have become essential information and technical assistants. A flexible interpreter engine helps you efficiently complete most tasks without complex development processes.

QPython offers an amazing developing experience - with its help, you could implement programs easily without complex IDE installation, compiling, or packaging processes.

"},{"location":"getting-started/#qpython-branches","title":"QPython Branches","text":"

For different usage scenarios, QPython has several branches:

  • QPython \u2013 The main version maintained by the QPython team with AI features, available on Google Play and other app stores
  • QPython+ \u2013 Community version launched by open-source contributors, offering various new features
  • QPython Plus \u2013 Extended permissions version (not available on app stores)
"},{"location":"getting-started/#key-features","title":"Key Features","text":"
  • Offline Python 3.12 interpreter - Run Python programs without Internet
  • SL4A Integration - Control Android hardware and APIs with Python
  • GenAI Integration - Support for local LLM, various LLM libraries including OpenAI, and AIPyApp for Vibe Coding development on QPython
  • Package Installation - Install extensions via QPYPI and pip
  • Built-in Editor - Syntax highlighting and code editing
  • Multiple Runtime Modes - Besides console programs, supports Android native UI (via SL4A interface), Pygame / Turtle / Tkinter and other runtime modes
"},{"location":"getting-started/#1-dashboard","title":"1. Dashboard","text":"

After you install QPython, start it by tapping its icon. You will see the main dashboard with the QPython logo and the following features:

"},{"location":"getting-started/#dashboard-features","title":"Dashboard Features","text":"

The QPython dashboard provides quick access to all major features:

  • Terminal \u2014 Access the Python console and shell for direct command execution
  • Notebook \u2014 Interactive Jupyter-style notebooks for data analysis and experiments
  • Editor \u2014 Built-in code editor with syntax highlighting for writing Python scripts
  • Explorer \u2014 Browse and manage your files, scripts, and projects
  • QPYPI \u2014 Install Python packages and extensions. See QPYPI Guide for details
  • Setting \u2014 Configure QPython preferences and runtime options
  • Community \u2014 Access QPython community resources, forums, and help
  • Courses \u2014 Access learning materials and tutorials for Python programming

Tap any icon to access the corresponding feature.

"},{"location":"getting-started/#2-terminal-and-editor","title":"2. Terminal and Editor","text":""},{"location":"getting-started/#terminal","title":"Terminal","text":"

The Terminal provides a Python console with: - Explore object properties - Test syntax and ideas - Execute commands directly

Use the plus button (1) to open new Terminal tabs, switch between them via the dropdown (2), and close with the close button (3).

"},{"location":"getting-started/#editor","title":"Editor","text":"

The editor's bottom toolbar contains the following tools (left to right):

  • Quick Input (includes keywords like def / if / else / elif / class)
  • Lock (prevent accidental touches)
  • Jump
  • Save
  • Run
  • Search
  • Undo
  • Redo
  • Save As
  • Recent Files
  • Code Snippets

Important: When saving, manually add the .py extension as the editor doesn't add it automatically.

"},{"location":"getting-started/#3-explorer-file-management","title":"3. Explorer (File Management)","text":"

Access scripts and projects through the Explorer, supporting browsing, organizing, and managing all Python files.

"},{"location":"getting-started/#scripts","title":"Scripts","text":"

Scripts are single Python files stored in /storage/emulated/0/Android/data/org.qpython.qpy/files/scripts3/ (for Python 3).

Available actions: - Run \u2014 Execute the script - Open \u2014 Edit with built-in editor - Rename \u2014 Change the script name - Delete \u2014 Remove the script

"},{"location":"getting-started/#projects","title":"Projects","text":"

Projects are directories containing main.py as the entry point. You can include other dependencies and resources in the same directory. Store projects in /storage/emulated/0/Android/data/org.qpython.qpy/files/projects3/.

"},{"location":"getting-started/#notebooks","title":"Notebooks","text":"

Jupyter-style notebooks are also managed through the Explorer, stored in /storage/emulated/0/Android/data/org.qpython.qpy/files/notebooks/.

Available actions: - Run \u2014 Execute the notebook - Open \u2014 Explore notebook content - Rename \u2014 Change the notebook name - Delete \u2014 Remove the notebook

"},{"location":"getting-started/#4-libraries","title":"4. Libraries","text":"

Extend QPython's capabilities by installing third-party libraries.

"},{"location":"getting-started/#package-installation-methods","title":"Package Installation Methods","text":"

QPYPI (Recommended)

Install pre-built libraries from QPYPI, including scientific packages like numpy, scipy, etc.

See QPYPI Guide for details.

PIP Client

Install pure Python libraries through QPython's PIP client or QPYPI interface:

pip install requests\n

Pre-compiled Packages

For packages with C/C++/Rust dependencies, use QPython's pre-compiled packages:

pip install numpy-qpython\npip install scipy-aipy\n

See QPYPI Guide for the full list of available packages.

Manual Installation

You can also copy libraries to /storage/emulated/0/Android/data/org.qpython.qpy/files/lib/python3.12/site-packages/.

"},{"location":"getting-started/#5-runtime-modes","title":"5. Runtime Modes","text":"

QPython supports several runtime modes for different use cases:

"},{"location":"getting-started/#console-mode","title":"Console Mode","text":"

Default mode for regular Python scripts.

"},{"location":"getting-started/#sl4a-mode","title":"SL4A Mode","text":"

Scripts that call Android APIs through the SL4A library.

import androidhelper\n\ndroid = androidhelper.Android()\ndroid.makeToast('Hello Android!')\n

See QSL4A Documentation for full API reference.

"},{"location":"getting-started/#webapp-mode","title":"WebApp Mode","text":"

Create web-based applications with a backend server. Add the following two headers at the beginning of your script:

#qpy:webapp:<project name>\n#qpy://localhost:<port the web service listens on>/<default main path>\n

Example:

#qpy:webapp:Hello QPython\n#qpy://localhost:8080/hello\n\nfrom bottle import route, run, Bottle\n\napp = Bottle()\n\n@route('/hello')\ndef hello():\n    return '<h1>Hello from QPython!</h1>'\n\nrun(app, host='localhost', port=8080)\n

"},{"location":"getting-started/#q-mode-quiet-mode","title":"Q Mode (Quiet Mode)","text":"

Run scripts silently without displaying the console. Add header at the beginning of your script:

#qpy:quiet\n\nimport time\n\nwhile True:\n    # Your background task\n    time.sleep(60)\n
If you need to run a GUI-based SL4A program and don't want to show console information, this mode is recommended.

"},{"location":"getting-started/#6-community-and-support","title":"6. Community and Support","text":"

Visit QPython.org for documentation, user communities, and help.

Community Links: - Facebook Group - GitHub - Report Issues

Next Steps: - Try the Hello World Tutorial - Explore QSL4A API for Android integration - Learn about QPython Branches

"},{"location":"getting-started/#video-introduction","title":"Video Introduction","text":""},{"location":"getting-started/#next-steps","title":"Next Steps","text":"

If you've got a basic understanding of QPython's features, welcome to start experiencing the fun of programming! Try the Hello World Tutorial to take your first step.

"},{"location":"qpypi-guide/","title":"QPYPI","text":"

You can extend your QPython capabilities by installing packages.

"},{"location":"qpypi-guide/#package-installation-support","title":"Package Installation Support","text":""},{"location":"qpypi-guide/#pure-python-packages","title":"Pure Python Packages","text":"

QPython supports Python packages developed with pure Python. You can install these packages directly using pip install through QPython's PIP Client or QPYPI on dashboard.

"},{"location":"qpypi-guide/#pre-compiled-packages","title":"Pre-compiled Packages","text":"

If some packages (or their dependencies) are developed with Rust/C/C++, QPython cannot support them directly because there is no compiler toolchain support on QPython. However, the QPython team has pre-compiled some commonly used packages and published them in QPython's QPYPI/Extensions for users to install easily.

"},{"location":"qpypi-guide/#installing-pre-compiled-packages","title":"Installing Pre-compiled Packages","text":"

You can install pre-compiled packages in the following ways:

  1. Through QPython App: Install directly from QPYPI or Extensions within the QPython app
  2. Through PyPI: Visit https://pypi.org/user/qpythonx/ to view available packages
  3. Via pip command:
  4. pip install xxx-qpython - Packages with the -qpython suffix
  5. pip install xxx-aipy - Packages with the -aipy suffix (typically AI/ML related packages)

Note: We usually add one of these suffixes based on the package's intended use case.

"},{"location":"qpypi-guide/#requesting-new-packages","title":"Requesting New Packages","text":"

If you need a package that is not currently supported:

  • Raise an issue in the qpython.org project
  • The QPython team will consider pre-compiling and adding it to the repository

For more ways to get help and engage with the community, see the Community & Feedback section.

Note: Because of different computer architectures, we cannot guarantee that QPYPI includes all packages from PyPI.

"},{"location":"qpython-x/","title":"QPython Branches","text":"

QPython is the Python engine for Android. It contains amazing features such as Python interpreter, runtime environment, editor, QPYI and integrated SL4A. It makes it easy for you to use Python on Android. And it's FREE.

QPython already has millions of users worldwide and it is also an open source project.

For different usage scenarios, QPython has several branches:

"},{"location":"qpython-x/#qpython","title":"QPython","text":"

Standard Edition: Optimized for AI performance and universal app store compatibility

The main version available on Google Play and other app stores. This version focuses on AI features, making it easier for users to learn and use Python in the AI era.

Key Features: - AI-powered coding assistance and learning tools - Offline Python 3.12 interpreter: no Internet is required to run Python programs - Supports multiple project types: console, SL4A, webapp - Convenient QR code reader for transferring codes to your phone - QPYPI and custom repository for prebuilt wheel packages - Easy-to-use editor with syntax highlighting - Good documentation and community support

Permissions: Requires only basic phone permissions for installation.

Download: Available on Google Play and major app stores.

"},{"location":"qpython-x/#qpython_1","title":"QPython+","text":"

Community Edition: Openly supports various community-driven features; available on select app stores.

The community open-source version (in planning and preparation). This version is designed for contributors who want to participate in QPython project development and supports customization for different manufacturers.

Key Features: - Community-driven development - Support for vendor customization - More flexible configuration options - Open for contributors to join development

Permissions: Requires only basic phone permissions for installation.

Download: Will be available on Google Play and major app stores.

Note: This version is currently in planning and preparation phase. Stay tuned for updates!

"},{"location":"qpython-x/#qpython-plus","title":"QPython Plus","text":"

QPython+ Full Access Edition: Grants complete permissions to call all Android interfaces. Download link available exclusively via the official cloud drive.

A special version with extended permissions that provides maximum control over the device. This version is NOT published on app stores due to its sensitive permission requirements.

Key Features: - Full SL4A API access including sensitive features - SMS/Call control APIs - Advanced system integration - Maximum device control capabilities

Permissions: Requires extensive permissions including: - Bluetooth - Location (GPS) - Read/Send SMS - Call phone - Camera and microphone - System settings - And other sensitive permissions

Download: Not available on app stores. Distributed through special channels only.

Important: QPython will not use these permissions in background without your knowledge. If you get exceptions while using SL4A APIs, please check whether the relevant permissions are enabled in system settings.

"},{"location":"tutorial-hello-world/","title":"Writing \"Hello World\"","text":""},{"location":"tutorial-hello-world/#hello-world","title":"Hello world","text":"

Well, after you became a bit more familiar with QPython, let's create our first program in QPython. Obviously, it will be helloworld.py. ;)

Start QPython, open editor and enter the following code:

#qpy:quiet\nimport androidhelper\n\ndroid = androidhelper.Android()\ndroid.makeToast('Hello, QPYTHON!')\n

No wonder, it's just similar to any other hello-world program. When executed, it just shows pop-up message on the screen (see screenshot on the top). Anyway, it's a good example of QPython program.

"},{"location":"tutorial-hello-world/#code-understanding","title":"Code Understanding","text":"

It begins with import androidhelper \u2014 the most useful module in QPython, which encapsulates almost all interface with Android available in Python. Any script developed in QPython starts with this statement (at least if it claims to communicate with user). Read more about Python library here and import statement here.

Next, we create an object droid (actually a class), which is necessary to call RPC functions to communicate with Android.

The last line of our code calls droid.makeToast(), which shows a small pop-up message (a \"toast\") on the screen.

Well, let's add some more functionality. Let it ask the user name and greet them.

"},{"location":"tutorial-hello-world/#more-samples","title":"More samples","text":"

We can display a simple dialog box with the title, prompt, edit field and buttons Ok and Cancel using dialogGetInput call. Replace the last line of your code and save it as hello1.py:

#qpy:quiet\nimport androidhelper\n\ndroid = androidhelper.Android()\nrespond = droid.dialogGetInput(\"Hello\", \"What is your name?\")\n

Well, I think it should return any respond, any user reaction. That's why I wrote respond = .... But what does the call actually return? Let's check. Just add print statement after the last line:

#qpy:quiet\nimport androidhelper\n\ndroid = androidhelper.Android()\nrespond = droid.dialogGetInput(\"Hello\", \"What is your name?\")\nprint(respond)\n

Then save and run it...

Oops! Nothing printed? Don't worry. Just pull notification bar and you will see \"QPython Program Output: hello1.py\" \u2014 tap it!

As you can see, droid.dialogGetInput() returns a JSON object with three fields. We need only one \u2014 result which contains an actual input from user.

Let's add script's reaction:

#qpy:quiet\nimport androidhelper\n\ndroid = androidhelper.Android()\nrespond = droid.dialogGetInput(\"Hello\", \"What is your name?\")\nprint(respond)\nmessage = f'Hello, {respond.result}!'\ndroid.makeToast(message)\n

Last two lines (1) format the message and (2) show the message to the user in the toast. See Python docs if you still don't know what f-strings mean.

Wow! It works! ;)

Now I'm going to add a bit of logic there. Think: what happen if the user clicks Cancel button, or clicks Ok leaving the input field blank?

You can play with the program checking what contains respond variable in every case.

First of all, I want to put text entered by user to a separate variable: name = respond.result. Then I'm going to check it, and if it contains any real text, it will be considered as a name and will be used in greeting. Otherwise another message will be shown. Replace fifth line message = f'Hello, {respond.result}!' with the following code:

name = respond.result\nif name:\n    message = f'Hello, {name}!'\nelse:\n    message = \"Hey! And you're not very polite, %Username%!\"\n

Use < and > buttons on the toolbar to indent/unindent lines in if-statement (or just use space/backspace keys). You can read more about indentation in Python here; if-statement described here.

First of all, we put user input to the variable name. Then we check does name contain anything? In case the user left the line blank and clicked Ok, the return value is empty string ''. In case of Cancel button pressed, the return value is None. Both are treated as false in if-statement. So, only if name contains anything meaningful, then-statement is executed and greeting \"Hello, ...!\" shown. In case of empty input the user will see \"Hey! And you're not very polite, %Username%!\" message.

Ok, here is the whole program:

#qpy:quiet\nimport androidhelper\n\ndroid = androidhelper.Android()\nrespond = droid.dialogGetInput(\"Hello\", \"What is your name?\")\nprint(respond)\nname = respond.result\nif name:\n    message = f'Hello, {name}!'\nelse:\n    message = \"Hey! And you're not very polite, %Username%!\"\ndroid.makeToast(message)\n
"},{"location":"tutorial-hello-world/#execution-result","title":"Execution Result","text":""},{"location":"tutorial-hello-world/#next-steps","title":"Next Steps","text":"

For Python beginners, we recommend learning from the Python Basic Syntax course to further your Python skills, or browse QPython Featured Courses to find more content you want to learn.

"},{"location":"whats-new/","title":"What's New","text":""},{"location":"whats-new/#v400","title":"v4.0.0","text":"
  • External storage access: Users can now save Python scripts directly to external storage, greatly improving file management flexibility.
  • QSL4A enhancements: Improved QSL4A features. (https://www.qpython.org/en/qsl4a/)
  • Community & Courses: Refined community and course modules with clearer info and better navigation for easier access to learning resources and support.
"},{"location":"whats-new/#v392-v393","title":"v3.9.2 / v3.9.3","text":"
  • SDK upgrade with support for 16 KB page size, providing a smoother runtime environment
  • Major editor updates for a more fluid editing experience
  • Various other minor improvements
"},{"location":"whats-new/#v391","title":"v3.9.1","text":"
  • SDK upgrade with support for 16 KB page size, providing a smoother runtime environment
  • Extension packages now support MCP
  • Bug fixes
"},{"location":"whats-new/#v390","title":"v3.9.0","text":"
  • SDK upgrade to incorporate the latest Android features
  • Built-in Ollama 0.9.5 upgrade: Now supports running Gemma3n models locally in QPython, experiencing more powerful on-device AI computing capabilities
  • Pygame game development support: Combined with XServer, you can now easily write and run Pygame games on QPython, unleashing your creativity!
  • Personalized icon customization: Customize your desktop icons and themes through QPython settings, creating a unique programming environment
"},{"location":"whats-new/#v3811","title":"v3.8.11","text":"
  • Built-in Ollama 0.9.5 upgrade: Now supports running Gemma3n models locally in QPython, experiencing more powerful on-device AI computing capabilities
  • Pygame game development support: Combined with XServer, you can now easily write and run Pygame games on QPython, unleashing your creativity!
  • Personalized icon customization: Customize your desktop icons and themes through QPython settings, creating a unique programming environment
"},{"location":"whats-new/#v3810","title":"v3.8.10","text":"
  • Built-in Ollama upgrade: Now you can easily run Qwen3 / Gemma3 models locally in QPython!
  • Powerful MCP library added: Added mcp library in Extensions -> AIPY, allowing you to take powerful MCP functionality with you anywhere, anytime!
"},{"location":"whats-new/#v389","title":"v3.8.9","text":"

Major update! AI programming fully integrated into QPython to make your programming easier!

  • Natural language programming support: New support for AIPyApp, enabling natural language programming in QPython. Currently in beta - join our community for usage instructions
  • New QSL4A feature: Added notebookOpen function, supporting natural language control to open Notebook files
  • Built-in editor upgrade: Enhanced editor functionality, supporting opening and browsing various text files
  • Convenient file management: Added internal storage entry in file manager for quick access to your files
"},{"location":"whats-new/#v388","title":"v3.8.8","text":"
  • Important improvements to file access permission controls, allowing users to flexibly enable or disable access to external storage files through QPython programming
  • SDK upgraded to enhance support and compatibility with newer Android versions
  • Added Anthropic and Google GenAI libraries in Extensions -> AIPY
"},{"location":"whats-new/#v387","title":"v3.8.7","text":"
  • Based on existing Tkinter support, added sample code for drawing Doraemon using Turtle in Extensions -> Tools
  • Optimized phone permission acquisition process, improving user experience and operational convenience
  • Added Google Gen AI library in Extensions -> AIPY for easy access to Gemini Developer API and Vertex AI
"},{"location":"whats-new/#v386","title":"v3.8.6","text":"
  • Tkinter support: Now supports Tkinter library, accessible through XServer. Please visit www.qpython.org's drive link \"Extra\" to download required files
  • Storage permission update: To improve user experience, we added permission to read phone storage for accessing Python programs stored in other directories

After this update, QPython and QPythonPlus (not available on app stores, designed for advanced users with more sensitive permissions) will maintain synchronized version numbers.

"},{"location":"whats-new/#v352-2025225","title":"v3.5.2 (2025/2/25)","text":"

Achieve seamless integration with the open source LLM deployment tool Ollama and DeepSeek developed by DeepSeek!

Features: - Zero threshold to run various large language models locally on mobile devices - Quickly deploy cutting-edge AI models such as DeepSeek - Enjoy a minimalist API calling experience - Build a completely offline intelligent programming environment

Capabilities: - Load/manage LLM models directly on the mobile phone - Real-time low-latency response based on local computing

"},{"location":"whats-new/#v350","title":"v3.5.0","text":"
  • Python upgraded to 3.12.8
  • Improved Dashboard for clearer, more user-friendly functionality
  • Added numerous third-party packages: PyTorch / Twisted / Scrapy / FastAPI ...
"},{"location":"whats-new/#v338","title":"v3.3.8","text":"
  • Fixed NumPy compatibility issues
"},{"location":"whats-new/#v335","title":"v3.3.5","text":"
  • Upgraded Python kernel to 3.11.9
  • Fixed bug where module installation status was not displayed in extensions
  • Fixed bug where deleting modules in extensions failed
  • Fixed other bugs
"},{"location":"whats-new/#v334","title":"v3.3.4","text":"
  • Added OpenAI/Langchain/APIGPTCloud AI packages
  • Removed unnecessary files to reduce size
"},{"location":"whats-new/#v325","title":"v3.2.5","text":"
  • Added some SL4A functions
  • Other bug fixes
"},{"location":"whats-new/#v323","title":"v3.2.3","text":"
  • Updated Python version to 3.11.0
  • Updated IPython version to 8.6.0
  • Updated pip version to 22.3.1
  • Updated scientific computing packages with automatic soft links
  • Reduced space usage
  • Added some SL4A functions + other bug fixes
"},{"location":"whats-new/#v318","title":"v3.1.8","text":"
  • Added operation hotkeys in terminal
  • Fixed issue where editor lost content on rotation
  • Fixed other bugs

Download on Google Play

"},{"location":"qsl4a/","title":"QSL4A (Scripting Layer for Android) API Documentation","text":"

QSL4A is QPython's scripting layer for Android, allowing you to control Android device features using Python.

"},{"location":"qsl4a/#quick-start","title":"Quick Start","text":"
import androidhelper\n\ndroid = androidhelper.Android()\n\n# Show a toast message\ndroid.makeToast('Hello QPython!')\n\n# Vibrate the device\ndroid.vibrate(500)\n\n# Get battery level (start monitoring first)\nimport time\ndroid.batteryStartMonitoring()\ntime.sleep(0.5)  # Wait for data\nbattery = droid.readBatteryData().result\nprint(f\"Battery: {battery['level']}%\")\n
"},{"location":"qsl4a/#documentation-structure","title":"Documentation Structure","text":""},{"location":"qsl4a/#core-modules","title":"Core Modules","text":"
  • Android Base - Core connection and RPC
  • Intent System - Android Intent operations
  • Event System - Event handling and broadcasting
"},{"location":"qsl4a/#ui-components","title":"UI Components","text":"
  • Dialogs - Alert, input, choice dialogs
  • FullScreen UI - Custom layout UI
  • FloatView - Floating window
  • Accessibility - Screen automation
"},{"location":"qsl4a/#system","title":"System","text":"
  • Battery - Battery monitoring
  • Sensors - Device sensors
  • Application - App management
  • System Info - Device information
  • Settings - System settings
  • WakeLock - Wake lock control
  • QPython Interface - Script execution
  • Activity Result - Activity result handling
"},{"location":"qsl4a/#hardware","title":"Hardware","text":"
  • Bluetooth - Bluetooth operations
  • Camera - Photo and video capture
  • Audio/Recorder - Audio recording
  • Webcam - MJPEG streaming
  • USB Serial - USB host serial
"},{"location":"qsl4a/#connectivity","title":"Connectivity","text":"
  • WiFi - WiFi operations
  • Location - GPS and location
  • SMS - SMS operations
  • Phone - Phone calls and info
  • Contacts - Contact management
  • Signal Strength - Signal monitoring
  • FTP Server - Built-in FTP server
"},{"location":"qsl4a/#storage","title":"Storage","text":"
  • DocumentFile - File operations
  • Clipboard - Clipboard operations
  • Preferences - Shared preferences
"},{"location":"qsl4a/#media","title":"Media","text":"
  • Media Player - Audio/Video playback
  • Image Processing - Image operations
"},{"location":"qsl4a/#special-features","title":"Special Features","text":"
  • Cipher - Encryption/Decryption
  • PGPT AI - AI speech services
"},{"location":"qsl4a/#result-object","title":"Result Object","text":"

Most QSL4A methods return a Result namedtuple with: - id - Request ID - result - The actual result data - error - Error message if failed

result = droid.getClipboard()\nif result.error is None:\n    print(result.result)\nelse:\n    print(f\"Error: {result.error}\")\n
"},{"location":"qsl4a/connectivity/contacts/","title":"Contacts API","text":"

Access and manage device contacts.

"},{"location":"qsl4a/connectivity/contacts/#contact-picking","title":"Contact Picking","text":""},{"location":"qsl4a/connectivity/contacts/#pickcontact","title":"pickContact()","text":"

Display a list of contacts to pick from.

pickContact()\n

Returns: Intent with contact URI

"},{"location":"qsl4a/connectivity/contacts/#pickphone","title":"pickPhone()","text":"

Display a list of phone numbers to pick from.

pickPhone()\n

Returns: Selected phone number string

"},{"location":"qsl4a/connectivity/contacts/#contact-queries","title":"Contact Queries","text":""},{"location":"qsl4a/connectivity/contacts/#contactsget","title":"contactsGet()","text":"

Get all contacts.

contactsGet(attributes=None)\n

Parameters: - attributes (list, optional): Specific attributes to retrieve

Returns: List of contact JSONObject

"},{"location":"qsl4a/connectivity/contacts/#contactsgetbyid","title":"contactsGetById()","text":"

Get a contact by ID.

contactsGetById(id, attributes=None)\n

Parameters: - id (int): Contact ID - attributes (list, optional): Specific attributes to retrieve

Returns: JSONObject contact data

"},{"location":"qsl4a/connectivity/contacts/#contactsgetcount","title":"contactsGetCount()","text":"

Get the total number of contacts.

contactsGetCount()\n

Returns: Integer count

"},{"location":"qsl4a/connectivity/contacts/#contactsgetids","title":"contactsGetIds()","text":"

Get all contact IDs.

contactsGetIds()\n

Returns: List of contact ID integers

"},{"location":"qsl4a/connectivity/contacts/#contactsgetattributes","title":"contactsGetAttributes()","text":"

Get all possible contact attributes.

contactsGetAttributes()\n

Returns: List of attribute names

"},{"location":"qsl4a/connectivity/contacts/#content-queries","title":"Content Queries","text":""},{"location":"qsl4a/connectivity/contacts/#querycontent","title":"queryContent()","text":"

Query content resolver with custom parameters.

queryContent(uri, attributes=None, selection=None, selectionArgs=None, order=None)\n

Parameters: - uri (str): Content URI - attributes (list, optional): Attributes to retrieve - selection (str, optional): WHERE clause - selectionArgs (list, optional): Selection arguments - order (str, optional): ORDER BY clause

Returns: List of JSONObject results

"},{"location":"qsl4a/connectivity/contacts/#queryattributes","title":"queryAttributes()","text":"

Get attributes for a content URI.

queryAttributes(uri)\n

Parameters: - uri (str): Content URI

Returns: JSONArray of attribute names

"},{"location":"qsl4a/connectivity/contacts/#usage-example","title":"Usage Example","text":"
import androidhelper\n\ndroid = androidhelper.Android()\n\n# Pick a contact\ncontact_uri = droid.pickContact().result\nprint(f\"Selected contact: {contact_uri}\")\n\n# Pick a phone number\nphone = droid.pickPhone().result\nprint(f\"Selected phone: {phone}\")\n\n# Get all contacts\ncontacts = droid.contactsGet().result\nprint(f\"Total contacts: {len(contacts)}\")\n\n# Get contact by ID\ncontact = droid.contactsGetById(1).result\nprint(f\"Contact: {contact}\")\n\n# Get contact attributes\nattrs = droid.contactsGetAttributes().result\nprint(f\"Available attributes: {attrs}\")\n
"},{"location":"qsl4a/connectivity/ftp/","title":"FTP Server API","text":"

Start and manage a built-in FTP server on the device.

"},{"location":"qsl4a/connectivity/ftp/#ftp-server-methods","title":"FTP Server Methods","text":""},{"location":"qsl4a/connectivity/ftp/#ftpstart","title":"ftpStart()","text":"

Start the FTP server.

ftpStart()\n

Returns: Array containing IP address and port [ip, port]

"},{"location":"qsl4a/connectivity/ftp/#ftpstop","title":"ftpStop()","text":"

Stop the FTP server.

ftpStop()\n
"},{"location":"qsl4a/connectivity/ftp/#ftpisrunning","title":"ftpIsRunning()","text":"

Check if FTP server is running.

ftpIsRunning()\n

Returns: True if running

"},{"location":"qsl4a/connectivity/ftp/#ftpget","title":"ftpGet()","text":"

Get FTP server IP address.

ftpGet()\n

Returns: Array with IP address and port

"},{"location":"qsl4a/connectivity/ftp/#ftpset","title":"ftpSet()","text":"

Configure FTP server settings.

ftpSet(port=None, rootDir=None, username=None, password=None)\n

Parameters: - port (int, optional): Server port - rootDir (str, optional): Root directory to serve - username (str, optional): Login username - password (str, optional): Login password

Returns: JSONObject with current settings

"},{"location":"qsl4a/connectivity/ftp/#ftpstatus","title":"ftpStatus()","text":"

Get FTP server status.

ftpStatus()\n

Returns: String status description

"},{"location":"qsl4a/connectivity/ftp/#usage-example","title":"Usage Example","text":"
import androidhelper\n\ndroid = androidhelper.Android()\n\n# Configure FTP server\ndroid.ftpSet(\n    port=2121,\n    rootDir=\"/sdcard\",\n    username=\"admin\",\n    password=\"secret\"\n)\n\n# Start FTP server\ninfo = droid.ftpStart().result\nprint(f\"FTP running at {info[0]}:{info[1]}\")\n\n# Check status\nif droid.ftpIsRunning().result:\n    print(\"FTP server is running\")\n\n# Get server info\nserver_info = droid.ftpGet().result\nprint(f\"Server: {server_info}\")\n\n# Stop when done\ndroid.ftpStop()\n

Note: Connect to the FTP server using any FTP client with the provided credentials.

"},{"location":"qsl4a/connectivity/location/","title":"Location API","text":"

Access GPS and network location services.

"},{"location":"qsl4a/connectivity/location/#methods","title":"Methods","text":""},{"location":"qsl4a/connectivity/location/#startlocating","title":"startLocating()","text":"

Start location updates.

startLocating(minUpdateTime=60000, minUpdateDistance=30, updateGnssStatus=False)\n

Parameters: - minUpdateTime (int): Minimum time between updates in milliseconds (default: 60000) - minUpdateDistance (float): Minimum distance for update in meters (default: 30) - updateGnssStatus (bool): Enable GNSS status updates (default: False)

"},{"location":"qsl4a/connectivity/location/#stoplocating","title":"stopLocating()","text":"

Stop location updates.

stopLocating()\n
"},{"location":"qsl4a/connectivity/location/#readlocation","title":"readLocation()","text":"

Get last known location.

readLocation()\n

Returns: Location data dict

"},{"location":"qsl4a/connectivity/location/#getlastknownlocation","title":"getLastKnownLocation()","text":"

Get cached location.

getLastKnownLocation()\n

Returns: Location from all providers

"},{"location":"qsl4a/connectivity/location/#geocode","title":"geocode()","text":"

Convert address to coordinates.

geocode(address, maxResults=1)\n
"},{"location":"qsl4a/connectivity/location/#location-provider-methods","title":"Location Provider Methods)","text":""},{"location":"qsl4a/connectivity/location/#locationproviders","title":"locationProviders()","text":"

Get available location providers on the phone.

locationProviders()\n

Returns: List of available provider names (e.g., ['gps', 'network'])

"},{"location":"qsl4a/connectivity/location/#locationproviderenabled","title":"locationProviderEnabled()","text":"

Check if a specific location provider is enabled.

locationProviderEnabled(provider)\n

Parameters: - provider (str): Provider name (e.g., 'gps', 'network')

Returns: True if enabled, False otherwise

"},{"location":"qsl4a/connectivity/location/#readgnssstatus","title":"readGnssStatus()","text":"

Read Global Navigation Satellite System status (requires Android 8+).

readGnssStatus()\n

Returns: JSONArray containing GNSS satellite information

"},{"location":"qsl4a/connectivity/location/#usage-example","title":"Usage Example","text":"
import androidhelper\nimport time\n\ndroid = androidhelper.Android()\n\n# Start location updates\ndroid.startLocating(minUpdateTime=5000, minUpdateDistance=1, updateGnssStatus=False)\n\n# Wait for fix\ntime.sleep(10)\n\n# Get location\nloc = droid.readLocation().result\nif loc:\n    lat = loc['latitude']\n    lon = loc['longitude']\n    print(f\"Location: {lat}, {lon}\")\n\ndroid.stopLocating()\n
"},{"location":"qsl4a/connectivity/phone/","title":"Phone API","text":"

Control phone calls and retrieve phone information.

"},{"location":"qsl4a/connectivity/phone/#phone-state-tracking","title":"Phone State Tracking","text":""},{"location":"qsl4a/connectivity/phone/#starttrackingphonestate","title":"startTrackingPhoneState()","text":"

Start tracking phone state changes. Generates 'phone' events.

startTrackingPhoneState()\n
"},{"location":"qsl4a/connectivity/phone/#readphonestate","title":"readPhoneState()","text":"

Read the current phone state.

readPhoneState()\n

Returns: Bundle with phone state and incoming number

"},{"location":"qsl4a/connectivity/phone/#stoptrackingphonestate","title":"stopTrackingPhoneState()","text":"

Stop tracking phone state.

stopTrackingPhoneState()\n
"},{"location":"qsl4a/connectivity/phone/#making-calls","title":"Making Calls","text":""},{"location":"qsl4a/connectivity/phone/#phonecall","title":"phoneCall()","text":"

Call a contact/phone number by URI.

phoneCall(uri)\n

Parameters: - uri (str): Contact URI or phone number URI

"},{"location":"qsl4a/connectivity/phone/#phonecallnumber","title":"phoneCallNumber()","text":"

Call a phone number directly.

phoneCallNumber(phone_number)\n

Parameters: - phone_number (str): Phone number to call

"},{"location":"qsl4a/connectivity/phone/#phonedial","title":"phoneDial()","text":"

Dial a number (opens dialer without calling).

phoneDial(uri)\n

Parameters: - uri (str): Contact URI or phone number URI

"},{"location":"qsl4a/connectivity/phone/#phonedialnumber","title":"phoneDialNumber()","text":"

Dial a phone number (opens dialer without calling).

phoneDialNumber(phone_number)\n

Parameters: - phone_number (str): Phone number

"},{"location":"qsl4a/connectivity/phone/#cell-location","title":"Cell Location","text":""},{"location":"qsl4a/connectivity/phone/#getcelllocation","title":"getCellLocation()","text":"

Get the current cell location.

getCellLocation()\n

Returns: JSONObject with cell location data

"},{"location":"qsl4a/connectivity/phone/#getallcellslocation","title":"getAllCellsLocation()","text":"

Get all cell locations (for dual SIM devices).

getAllCellsLocation()\n

Returns: JSONArray of cell locations

"},{"location":"qsl4a/connectivity/phone/#network-information","title":"Network Information","text":""},{"location":"qsl4a/connectivity/phone/#getnetworkoperator","title":"getNetworkOperator()","text":"

Get the MCC+MNC of the current operator.

getNetworkOperator()\n

Returns: String (e.g., '310260')

"},{"location":"qsl4a/connectivity/phone/#getnetworkoperatorname","title":"getNetworkOperatorName()","text":"

Get the name of the current operator.

getNetworkOperatorName()\n

Returns: String (e.g., 'T-Mobile')

"},{"location":"qsl4a/connectivity/phone/#getnetworktype","title":"getNetworkType()","text":"

Get the current network type.

getNetworkType()\n

Returns: String describing radio technology (e.g., 'LTE', 'UMTS', 'GSM')

"},{"location":"qsl4a/connectivity/phone/#getphonetype","title":"getPhoneType()","text":"

Get the phone type.

getPhoneType()\n

Returns: String (e.g., 'GSM', 'CDMA', 'SIP')

"},{"location":"qsl4a/connectivity/phone/#sim-information","title":"SIM Information","text":""},{"location":"qsl4a/connectivity/phone/#getsimcountryiso","title":"getSimCountryIso()","text":"

Get the ISO country code for the SIM.

getSimCountryIso()\n

Returns: String (e.g., 'us')

"},{"location":"qsl4a/connectivity/phone/#getsimoperator","title":"getSimOperator()","text":"

Get the MCC+MNC of the SIM operator.

getSimOperator()\n

Returns: String (e.g., '310260')

"},{"location":"qsl4a/connectivity/phone/#getsimoperatorname","title":"getSimOperatorName()","text":"

Get the SIM operator name.

getSimOperatorName()\n

Returns: String (e.g., 'T-Mobile')

"},{"location":"qsl4a/connectivity/phone/#getsimserialnumber","title":"getSimSerialNumber()","text":"

Get the SIM serial number.

getSimSerialNumber()\n

Returns: String SIM serial number

"},{"location":"qsl4a/connectivity/phone/#getsimstate","title":"getSimState()","text":"

Get the SIM card state.

getSimState()\n

Returns: String describing SIM state

"},{"location":"qsl4a/connectivity/phone/#getsubscriberid","title":"getSubscriberId()","text":"

Get the subscriber ID.

getSubscriberId()\n

Returns: String subscriber ID

"},{"location":"qsl4a/connectivity/phone/#voice-mail","title":"Voice Mail","text":""},{"location":"qsl4a/connectivity/phone/#getvoicemailalphatag","title":"getVoiceMailAlphaTag()","text":"

Get the voice mail alpha tag.

getVoiceMailAlphaTag()\n

Returns: String voice mail tag

"},{"location":"qsl4a/connectivity/phone/#getvoicemailnumber","title":"getVoiceMailNumber()","text":"

Get the voice mail number.

getVoiceMailNumber()\n

Returns: String voice mail number

"},{"location":"qsl4a/connectivity/phone/#device-information","title":"Device Information","text":""},{"location":"qsl4a/connectivity/phone/#getdeviceid","title":"getDeviceId()","text":"

Get the device ID (IMEI for GSM). Deprecated.

getDeviceId()\n

Returns: String device ID

"},{"location":"qsl4a/connectivity/phone/#getdevicesoftwareversion","title":"getDeviceSoftwareVersion()","text":"

Get the device software version.

getDeviceSoftwareVersion()\n

Returns: String software version

"},{"location":"qsl4a/connectivity/phone/#getline1number","title":"getLine1Number()","text":"

Get the line 1 phone number.

getLine1Number()\n

Returns: String phone number

"},{"location":"qsl4a/connectivity/phone/#checknetworkroaming","title":"checkNetworkRoaming()","text":"

Check if connected to roaming network.

checkNetworkRoaming()\n

Returns: True if roaming

"},{"location":"qsl4a/connectivity/phone/#cell-info","title":"Cell Info","text":""},{"location":"qsl4a/connectivity/phone/#getallcellinfo","title":"getAllCellInfo()","text":"

Get information about all cells.

getAllCellInfo()\n

Returns: List of cell information

"},{"location":"qsl4a/connectivity/phone/#setdataenabled","title":"setDataEnabled()","text":"

Enable or disable mobile data.

setDataEnabled(enabled)\n

Parameters: - enabled (bool): True to enable, False to disable

"},{"location":"qsl4a/connectivity/phone/#usage-example","title":"Usage Example","text":"
import androidhelper\n\ndroid = androidhelper.Android()\n\n# Get network info\noperator = droid.getNetworkOperatorName().result\nprint(f\"Operator: {operator}\")\n\nnetwork_type = droid.getNetworkType().result\nprint(f\"Network: {network_type}\")\n\n# Get SIM info\nsim_state = droid.getSimState().result\nprint(f\"SIM: {sim_state}\")\n\n# Get phone number\nline1 = droid.getLine1Number().result\nprint(f\"Phone: {line1}\")\n\n# Track phone state\ndroid.startTrackingPhoneState()\nprint(\"Tracking phone state...\")\ndroid.stopTrackingPhoneState()\n
"},{"location":"qsl4a/connectivity/signalstrength/","title":"Signal Strength API","text":"

Monitor cellular and wireless signal strength.

"},{"location":"qsl4a/connectivity/signalstrength/#signal-strength-methods","title":"Signal Strength Methods","text":""},{"location":"qsl4a/connectivity/signalstrength/#starttrackingsignalstrengths","title":"startTrackingSignalStrengths()","text":"

Start tracking signal strength changes. Generates 'signal_strengths' events.

startTrackingSignalStrengths()\n
"},{"location":"qsl4a/connectivity/signalstrength/#stoptrackingsignalstrengths","title":"stopTrackingSignalStrengths()","text":"

Stop tracking signal strengths.

stopTrackingSignalStrengths()\n
"},{"location":"qsl4a/connectivity/signalstrength/#readsignalstrengths","title":"readSignalStrengths()","text":"

Read the current signal strengths.

readSignalStrengths()\n

Returns: Bundle with signal strength data

"},{"location":"qsl4a/connectivity/signalstrength/#gettelephonesignalstrengthlevel","title":"getTelephoneSignalStrengthLevel()","text":"

Get the telephone signal strength as a level (0-4).

getTelephoneSignalStrengthLevel()\n

Returns: Integer level (0=none, 1=poor, 2=fair, 3=good, 4=excellent)

"},{"location":"qsl4a/connectivity/signalstrength/#gettelephonesignalstrengthdetail","title":"getTelephoneSignalStrengthDetail()","text":"

Get detailed telephone signal strength information.

getTelephoneSignalStrengthDetail()\n

Returns: String with detailed signal info

"},{"location":"qsl4a/connectivity/signalstrength/#usage-example","title":"Usage Example","text":"
import androidhelper\nimport time\n\ndroid = androidhelper.Android()\n\n# Start tracking signal strength\ndroid.startTrackingSignalStrengths()\n\n# Wait for signal updates\ntime.sleep(5)\n\n# Read current signal strength\nsignal = droid.readSignalStrengths().result\nprint(f\"Signal: {signal}\")\n\n# Get level directly\nlevel = droid.getTelephoneSignalStrengthLevel().result\nprint(f\"Signal level: {level}/4\")\n\ndroid.stopTrackingSignalStrengths()\n
"},{"location":"qsl4a/connectivity/sms/","title":"SMS API","text":"

Send and receive SMS messages.

"},{"location":"qsl4a/connectivity/sms/#methods","title":"Methods","text":""},{"location":"qsl4a/connectivity/sms/#smssend","title":"smsSend()","text":"

Send SMS message.

smsSend(destinationAddress, text)\n

Parameters: - destinationAddress (str): Phone number - text (str): Message text

"},{"location":"qsl4a/connectivity/sms/#smsgetmessagecount","title":"smsGetMessageCount()","text":"

Get message count.

smsGetMessageCount(unreadOnly=False, folder=\"inbox\")\n
"},{"location":"qsl4a/connectivity/sms/#smsgetmessageids","title":"smsGetMessageIds()","text":"

Get message IDs.

smsGetMessageIds(unreadOnly=False, folder=\"inbox\")\n
"},{"location":"qsl4a/connectivity/sms/#smsgetmessages","title":"smsGetMessages()","text":"

Get message details.

smsGetMessages(unreadOnly=False, folder=\"inbox\", attributes=None)\n
"},{"location":"qsl4a/connectivity/sms/#smsgetmessagebyid","title":"smsGetMessageById()","text":"

Get a specific message by ID.

smsGetMessageById(id, attributes=None)\n

Parameters: - id (int): Message ID - attributes (list, optional): Specific attributes to retrieve

Returns: Message data dict

"},{"location":"qsl4a/connectivity/sms/#smsgetattributes","title":"smsGetAttributes()","text":"

Get available SMS message attributes.

smsGetAttributes()\n

Returns: List of available attribute names

"},{"location":"qsl4a/connectivity/sms/#smsdeletemessage","title":"smsDeleteMessage()","text":"

Delete message.

smsDeleteMessage(id)\n
"},{"location":"qsl4a/connectivity/sms/#smsmarkmessageread","title":"smsMarkMessageRead()","text":"

Mark message as read.

smsMarkMessageRead(ids, read=True)\n
"},{"location":"qsl4a/connectivity/sms/#usage-example","title":"Usage Example","text":"
import androidhelper\n\ndroid = androidhelper.Android()\n\n# Send SMS\ndroid.smsSend(\"+1234567890\", \"Hello from QPython!\")\n\n# Get unread messages\nmessages = droid.smsGetMessages(unreadOnly=True).result\nfor msg in messages:\n    print(f\"From: {msg['address']}, Text: {msg['body']}\")\n
"},{"location":"qsl4a/connectivity/wifi/","title":"WiFi API","text":"

Control WiFi adapter and get connection information.

"},{"location":"qsl4a/connectivity/wifi/#adapter-control","title":"Adapter Control","text":""},{"location":"qsl4a/connectivity/wifi/#checkwifistate","title":"checkWifiState()","text":"

Check if WiFi is enabled.

checkWifiState()\n

Returns: True if WiFi is enabled, False otherwise

"},{"location":"qsl4a/connectivity/wifi/#togglewifistate","title":"toggleWifiState()","text":"

Turn WiFi on or off.

toggleWifiState(enabled=None)\n

Parameters: - enabled (bool): True to enable, False to disable, None to toggle

Returns: True if operation succeeded

"},{"location":"qsl4a/connectivity/wifi/#wifistartscan","title":"wifiStartScan()","text":"

Start scanning for available WiFi networks.

wifiStartScan()\n
"},{"location":"qsl4a/connectivity/wifi/#wifigetscanresults","title":"wifiGetScanResults()","text":"

Get list of discovered WiFi networks.

wifiGetScanResults()\n

Returns: List of access point information

"},{"location":"qsl4a/connectivity/wifi/#connection-management","title":"Connection Management","text":""},{"location":"qsl4a/connectivity/wifi/#wifigetconnectioninfo","title":"wifiGetConnectionInfo()","text":"

Get detailed connection information.

wifiGetConnectionInfo()\n

Returns: Dict with connection details including SSID, BSSID, IP address

"},{"location":"qsl4a/connectivity/wifi/#getconnectedinfo","title":"getConnectedInfo()","text":"

Get connected WiFi network info (simplified).

getConnectedInfo()\n

Returns: Dict with SSID, BSSID, signal strength

"},{"location":"qsl4a/connectivity/wifi/#getdhcpinfo","title":"getDhcpInfo()","text":"

Get DHCP information for current connection.

getDhcpInfo(ipConvertToString=True)\n

Parameters: - ipConvertToString (bool): Convert IP addresses to string format (default: True)

Returns: Dict with DHCP info including IP, gateway, DNS

"},{"location":"qsl4a/connectivity/wifi/#wifidisconnect","title":"wifiDisconnect()","text":"

Disconnect from current WiFi network.

wifiDisconnect()\n
"},{"location":"qsl4a/connectivity/wifi/#wifireconnect","title":"wifiReconnect()","text":"

Reconnect to the current network.

wifiReconnect()\n
"},{"location":"qsl4a/connectivity/wifi/#wifireassociate","title":"wifiReassociate()","text":"

Reassociate with the current access point.

wifiReassociate()\n
"},{"location":"qsl4a/connectivity/wifi/#hotspot","title":"Hotspot","text":""},{"location":"qsl4a/connectivity/wifi/#wifigetapstate","title":"wifiGetApState()","text":"

Get WiFi AP (hotspot) state.

wifiGetApState()\n

Returns: Hotspot state

"},{"location":"qsl4a/connectivity/wifi/#wifi-locks","title":"WiFi Locks","text":""},{"location":"qsl4a/connectivity/wifi/#wifilockacquirefull","title":"wifiLockAcquireFull()","text":"

Acquire a full WiFi lock (keeps WiFi active even when screen is off).

wifiLockAcquireFull()\n
"},{"location":"qsl4a/connectivity/wifi/#wifilockacquirescanonly","title":"wifiLockAcquireScanOnly()","text":"

Acquire a scan-only WiFi lock.

wifiLockAcquireScanOnly()\n
"},{"location":"qsl4a/connectivity/wifi/#wifilockrelease","title":"wifiLockRelease()","text":"

Release the WiFi lock.

wifiLockRelease()\n
"},{"location":"qsl4a/connectivity/wifi/#usage-example","title":"Usage Example","text":"
import androidhelper\nimport time\n\ndroid = androidhelper.Android()\n\n# Check WiFi state\nif droid.checkWifiState().result:\n    print(\"WiFi is enabled\")\nelse:\n    print(\"Enabling WiFi...\")\n    droid.toggleWifiState(True)\n    time.sleep(2)\n\n# Start scanning\ndroid.wifiStartScan()\ntime.sleep(3)\n\n# Get scan results\nnetworks = droid.wifiGetScanResults().result\nfor network in networks:\n    print(f\"SSID: {network.get('SSID')}, Signal: {network.get('level')} dBm\")\n\n# Get connection info\ninfo = droid.wifiGetConnectionInfo().result\nif info:\n    print(f\"Connected to: {info.get('ssid')}\")\n    print(f\"BSSID: {info.get('bssid')}\")\n    print(f\"IP: {info.get('ip_address')}\")\n\n# Get DHCP info\ndhcp = droid.getDhcpInfo().result\nif dhcp:\n    print(f\"Gateway: {dhcp.get('gateway')}\")\n    print(f\"DNS: {dhcp.get('dns1')}\")\n\n# Get simplified connected info\nconnected = droid.getConnectedInfo().result\nprint(f\"SSID: {connected.get('ssid')}, Signal: {connected.get('level')} dBm\")\n\n# Disconnect and reconnect\ndroid.wifiDisconnect()\ntime.sleep(1)\ndroid.wifiReconnect()\n\n# Reassociate with access point\ndroid.wifiReassociate()\n\n# Check hotspot state\nap_state = droid.wifiGetApState().result\nprint(f\"Hotspot state: {ap_state}\")\n\n# Acquire WiFi lock for background operation\ndroid.wifiLockAcquireFull()\n# ... do work ...\ndroid.wifiLockRelease()\n\n# Or use scan-only lock for lighter background operation\ndroid.wifiLockAcquireScanOnly()\n# ... do scanning work ...\ndroid.wifiLockRelease()\n
"},{"location":"qsl4a/core/android-base/","title":"Android Base Class","text":"

The Android class is the core of QSL4A, providing the connection to the Android runtime and RPC mechanism.

"},{"location":"qsl4a/core/android-base/#module-import","title":"Module Import","text":"
# Full featured (recommended)\nimport androidhelper\ndroid = androidhelper.Android()\n\n# Minimal version (quick, single instance)\nimport android\ndroid = android.droid\n
"},{"location":"qsl4a/core/android-base/#class-android","title":"Class: Android","text":""},{"location":"qsl4a/core/android-base/#constructor","title":"Constructor","text":"
Android(addr=None)\n

Parameters: - addr (tuple, optional): (HOST, PORT) address. If None, uses environment variables AP_HOST and AP_PORT

Environment Variables: - AP_HOST - Server host address - AP_PORT - Server port - AP_HANDSHAKE - Authentication token

"},{"location":"qsl4a/core/android-base/#core-methods","title":"Core Methods","text":""},{"location":"qsl4a/core/android-base/#_rpc","title":"_rpc()","text":"

Internal RPC method for calling Android functions.

_rpc(method, *args)\n

Parameters: - method (str): Method name to call - *args: Variable arguments for the method

Returns: Result namedtuple with fields: - id (int): Request ID - result: Method return value - error (str or None): Error message if failed

"},{"location":"qsl4a/core/android-base/#getattr","title":"getattr()","text":"

Dynamic method dispatcher. All Android methods are accessed via attribute lookup.

# These are equivalent:\ndroid.makeToast(\"Hello\")\ndroid._rpc(\"makeToast\", \"Hello\")\n
"},{"location":"qsl4a/core/android-base/#standalone-functions-androidpy","title":"Standalone Functions (android.py)","text":"

When using the minimal android module:

"},{"location":"qsl4a/core/android-base/#jsla","title":"jsla()","text":"

Send JSON-RPC request and return raw response.

jsla(method, *params)\n

Returns: JSON string response

"},{"location":"qsl4a/core/android-base/#rsla","title":"rsla()","text":"

Send request and return result only.

rsla(method, *params)\n

Returns: Result value from RPC call

"},{"location":"qsl4a/core/android-base/#esla","title":"esla()","text":"

Send request, raise exception on error.

esla(method, *params)\n

Raises: Exception if error field is not None

"},{"location":"qsl4a/core/android-base/#nsla","title":"nsla()","text":"

Send request and return Result namedtuple.

nsla(method, *params)\n

Returns: Result namedtuple

"},{"location":"qsl4a/core/android-base/#usage-examples","title":"Usage Examples","text":""},{"location":"qsl4a/core/android-base/#basic-connection","title":"Basic Connection","text":"
import androidhelper\n\n# Connect to Android runtime\ndroid = androidhelper.Android()\n\n# Check connection by showing toast\ndroid.makeToast(\"Connected!\")\n
"},{"location":"qsl4a/core/android-base/#error-handling","title":"Error Handling","text":"
result = droid.someMethod()\nif result.error:\n    print(f\"Error: {result.error}\")\nelse:\n    print(f\"Success: {result.result}\")\n
"},{"location":"qsl4a/core/android-base/#direct-rpc-call","title":"Direct RPC Call","text":"
# Call any method directly\nresult = droid._rpc(\"makeToast\", \"Hello World\")\n
"},{"location":"qsl4a/core/events/","title":"Event System","text":"

QSL4A provides an event system for handling asynchronous events from Android, such as sensor updates, location changes, and custom broadcasts.

"},{"location":"qsl4a/core/events/#event-basics","title":"Event Basics","text":"

Events are stored in a buffer and can be polled or waited for. Each event has: - name: Event type/name - data: Event payload data - time: Timestamp

"},{"location":"qsl4a/core/events/#event-methods","title":"Event Methods","text":""},{"location":"qsl4a/core/events/#eventclearbuffer","title":"eventClearBuffer()","text":"

Clear all pending events from the buffer.

eventClearBuffer()\n

Returns: None

"},{"location":"qsl4a/core/events/#eventpoll","title":"eventPoll()","text":"

Poll for events in the buffer.

eventPoll(number_of_events=1)\n

Parameters: - number_of_events (int): Maximum events to retrieve (default: 1)

Returns: List of event objects

"},{"location":"qsl4a/core/events/#eventwait","title":"eventWait()","text":"

Wait for any event.

eventWait(timeout=None)\n

Parameters: - timeout (int, optional): Timeout in seconds. None = wait forever

Returns: Event object or None if timeout

"},{"location":"qsl4a/core/events/#eventwaitfor","title":"eventWaitFor()","text":"

Wait for a specific event.

eventWaitFor(eventName, timeout=None)\n

Parameters: - eventName (str): Name of event to wait for - timeout (int, optional): Timeout in seconds

Returns: Event object or None if timeout

"},{"location":"qsl4a/core/events/#eventpost","title":"eventPost()","text":"

Post a custom event.

eventPost(name, data, enqueue=None)\n

Parameters: - name (str): Event name - data: Event data (any type) - enqueue (bool, optional): Add to queue if True

"},{"location":"qsl4a/core/events/#receiveevent","title":"receiveEvent()","text":"

Receive event (blocking).

receiveEvent()\n

Returns: Event object

"},{"location":"qsl4a/core/events/#broadcast-events","title":"Broadcast Events","text":"

Register for system broadcast events.

"},{"location":"qsl4a/core/events/#eventregisterforbroadcast","title":"eventRegisterForBroadcast()","text":"

Register to receive broadcast events.

eventRegisterForBroadcast(category, enqueue=True)\n

Parameters: - category (str): Broadcast category/action - enqueue (bool): Add to event queue

"},{"location":"qsl4a/core/events/#eventunregisterforbroadcast","title":"eventUnregisterForBroadcast()","text":"

Unregister from broadcast events.

eventUnregisterForBroadcast(category)\n
"},{"location":"qsl4a/core/events/#eventgetbrodcastcategories","title":"eventGetBrodcastCategories()","text":"

Get registered broadcast categories.

eventGetBrodcastCategories()\n

Returns: List of registered categories

"},{"location":"qsl4a/core/events/#event-dispatcher","title":"Event Dispatcher)","text":""},{"location":"qsl4a/core/events/#starteventdispatcher","title":"startEventDispatcher()","text":"

Opens up a socket where you can read for events posted.)

startEventDispatcher(port=0)\n

Parameters: - port (int, optional): Port to listen on (default: 0 = auto-select)

Returns: Port number being listened on

"},{"location":"qsl4a/core/events/#stopeventdispatcher","title":"stopEventDispatcher()","text":"

Stops the event server.)

stopEventDispatcher()\n
"},{"location":"qsl4a/core/events/#deprecated-methods","title":"Deprecated Methods","text":""},{"location":"qsl4a/core/events/#rpcpostevent","title":"rpcPostEvent()","text":"

Post an event to the event queue. (Deprecated, use eventPost)

rpcPostEvent(name, data)\n

Parameters: - name (str): Event name - data: Event data

"},{"location":"qsl4a/core/events/#usage-examples","title":"Usage Examples","text":""},{"location":"qsl4a/core/events/#basic-event-polling","title":"Basic Event Polling","text":"
import androidhelper\n\ndroid = androidhelper.Android()\n\n# Clear old events\ndroid.eventClearBuffer()\n\n# Poll for events\nevents = droid.eventPoll(5).result\nfor event in events:\n    print(f\"Event: {event['name']}, Data: {event['data']}\")\n
"},{"location":"qsl4a/core/events/#waiting-for-events","title":"Waiting for Events","text":"
# Wait for any event with timeout\nevent = droid.eventWait(timeout=10).result\nif event:\n    print(f\"Got event: {event['name']}\")\n
"},{"location":"qsl4a/core/events/#waiting-for-specific-event","title":"Waiting for Specific Event","text":"
# Wait for sensor event\nevent = droid.eventWaitFor('screen', timeout=5).result\nif event:\n    print(f\"Screen event: {event['data']}\")\n
"},{"location":"qsl4a/core/events/#posting-custom-events","title":"Posting Custom Events","text":"
# Post custom event\ndroid.eventPost('my_event', {'key': 'value'})\n\n# Wait for it\nevent = droid.eventWaitFor('my_event', timeout=1).result\n
"},{"location":"qsl4a/core/events/#broadcast-receiver","title":"Broadcast Receiver","text":"
# Register for screen on/off events\ndroid.eventRegisterForBroadcast('android.intent.action.SCREEN_ON')\ndroid.eventRegisterForBroadcast('android.intent.action.SCREEN_OFF')\n\n# Wait for screen events\nwhile True:\n    event = droid.receiveEvent().result\n    if event['name'] == 'android.intent.action.SCREEN_ON':\n        print(\"Screen turned on\")\n    elif event['name'] == 'android.intent.action.SCREEN_OFF':\n        print(\"Screen turned off\")\n
"},{"location":"qsl4a/core/events/#sensor-event-handling","title":"Sensor Event Handling","text":"
# Start sensing\ndroid.startSensingTimed(1, 250)\n\n# Process sensor events\nfor _ in range(100):\n    event = droid.eventWait(timeout=1).result\n    if event and event['name'] == 'sensors':\n        data = event['data']\n        print(f\"Accel: {data['xforce']}, {data['yforce']}, {data['zforce']}\")\n\ndroid.stopSensing()\n
"},{"location":"qsl4a/core/events/#common-event-types","title":"Common Event Types","text":"Event Name Description Source sensors Sensor data update startSensing* location GPS location update startLocating phone Phone state change startTrackingPhoneState signal Signal strength change startTrackingSignalStrength screen Screenshot ready fullGetScreenShot dialog Dialog response dialogShow*"},{"location":"qsl4a/core/events/#event-data-structure","title":"Event Data Structure","text":"
{\n    'name': 'event_name',\n    'data': {\n        # Event-specific data\n    },\n    'time': 1234567890  # Timestamp\n}\n
"},{"location":"qsl4a/core/intent/","title":"Intent System","text":"

Android Intents are used to start activities, send broadcasts, and communicate between apps. QSL4A provides full Intent support through the Intent module.

"},{"location":"qsl4a/core/intent/#module-import","title":"Module Import","text":"
import androidhelper\ndon = androidhelper.Android()\n
"},{"location":"qsl4a/core/intent/#intent-constants","title":"Intent Constants","text":"

Access via droid.Intent:

"},{"location":"qsl4a/core/intent/#actions","title":"Actions","text":"Constant Value Usage ACTION_MAIN android.intent.action.MAIN App entry point ACTION_VIEW android.intent.action.VIEW View content ACTION_EDIT android.intent.action.EDIT Edit content ACTION_PICK android.intent.action.PICK Pick item ACTION_SEND android.intent.action.SEND Share content ACTION_SEARCH android.intent.action.SEARCH Search"},{"location":"qsl4a/core/intent/#flags","title":"Flags","text":"Constant Value Usage FLAG_ACTIVITY_NEW_TASK 268435456 Start new task FLAG_ACTIVITY_CLEAR_TASK 32768 Clear task stack FLAG_ACTIVITY_NEW_DOCUMENT 524288 New document mode"},{"location":"qsl4a/core/intent/#extras","title":"Extras","text":"Constant Usage EXTRA_TEXT Text content EXTRA_STREAM File URI EXTRA_SUBJECT Subject line EXTRA_EMAIL Email address"},{"location":"qsl4a/core/intent/#core-methods","title":"Core Methods","text":""},{"location":"qsl4a/core/intent/#makeintent","title":"makeIntent()","text":"

Create an Intent object.

makeIntent(action, uri=None, type=None, extras=None, categories=None,\n           packagename=None, classname=None, flags=None)\n

Parameters: - action (str): Intent action (e.g., droid.Intent.ACTION_VIEW) - uri (str, optional): Data URI - type (str, optional): MIME type - extras (dict, optional): Extra data - categories (list, optional): Intent categories - packagename (str, optional): Target package - classname (str, optional): Target class - flags (int, optional): Intent flags

Returns: Intent object

"},{"location":"qsl4a/core/intent/#startactivityintent","title":"startActivityIntent()","text":"

Start an Activity with an Intent.

startActivityIntent(intent, wait=None)\n

Parameters: - intent: Intent object from makeIntent() - wait (bool, optional): Block until activity closes

"},{"location":"qsl4a/core/intent/#startactivityforresultintent","title":"startActivityForResultIntent()","text":"

Start activity and wait for result.

startActivityForResultIntent(intent)\n

Returns: Activity result

"},{"location":"qsl4a/core/intent/#sendbroadcastintent","title":"sendBroadcastIntent()","text":"

Send a broadcast.

sendBroadcastIntent(intent)\n
"},{"location":"qsl4a/core/intent/#view","title":"view()","text":"

View content by URI.

view(uri, type=None, extras=None)\n
"},{"location":"qsl4a/core/intent/#pick","title":"pick()","text":"

Pick content from URI.

pick(uri)\n
"},{"location":"qsl4a/core/intent/#common-intent-methods","title":"Common Intent Methods)","text":""},{"location":"qsl4a/core/intent/#scanbarcode","title":"scanBarcode()","text":"

Launch the barcode scanner.

scanBarcode()\n

Returns: Scanned barcode string

"},{"location":"qsl4a/core/intent/#send","title":"send()","text":"

Send content via share intent.

send(type, content)\n

Parameters: - type (str): MIME type - content (str): Content to share

"},{"location":"qsl4a/core/intent/#sendtext","title":"sendText()","text":"

Send text content.

sendText(text)\n

Parameters: - text (str): Text to send

"},{"location":"qsl4a/core/intent/#sendemail","title":"sendEmail()","text":"

Send an email.

sendEmail(to, subject, body, attachment=None)\n

Parameters: - to (str or list): Recipient email address(es) - subject (str): Email subject - body (str): Email body - attachment (str, optional): Attachment file path

"},{"location":"qsl4a/core/intent/#pathtouri","title":"pathToUri()","text":"

Convert file path to content URI.

pathToUri(path)\n

Parameters: - path (str): File path

Returns: Content URI string

"},{"location":"qsl4a/core/intent/#openfile","title":"openFile()","text":"

Open a file with appropriate app.

openFile(path)\n

Parameters: - path (str): File path to open

"},{"location":"qsl4a/core/intent/#sendfile","title":"sendFile()","text":"

Send a file via share intent.

sendFile(path)\n

Parameters: - path (str): File path to send

"},{"location":"qsl4a/core/intent/#getpathtype","title":"getPathType()","text":"

Get the MIME type for a file path.

getPathType(path)\n

Parameters: - path (str): File path

Returns: MIME type string

"},{"location":"qsl4a/core/intent/#viewmap","title":"viewMap()","text":"

Open map at a location.

viewMap(latitude, longitude)\n

Parameters: - latitude (float): Latitude - longitude (float): Longitude

"},{"location":"qsl4a/core/intent/#viewcontacts","title":"viewContacts()","text":"

Open the contacts app.

viewContacts()\n
"},{"location":"qsl4a/core/intent/#search","title":"search()","text":"

Perform a web search.

search(query)\n

Parameters: - query (str): Search query

"},{"location":"qsl4a/core/intent/#viewhtml","title":"viewHtml()","text":"

View HTML content.

viewHtml(content, encoding=None)\n

Parameters: - content (str): HTML content - encoding (str, optional): Character encoding

"},{"location":"qsl4a/core/intent/#webviewshow","title":"webViewShow()","text":"

Display web content in WebView. Deprecated, use viewHtml.

webViewShow(url)\n

Parameters: - url (str): Web page URL

"},{"location":"qsl4a/core/intent/#editoropen","title":"editorOpen()","text":"

Open a text editor.

editorOpen(path=None, create=False)\n

Parameters: - path (str, optional): File path to edit - create (bool, optional): Create if doesn't exist

"},{"location":"qsl4a/core/intent/#helper-class-uri","title":"Helper Class: Uri","text":"

Create URI objects for Intents:

from androidhelper.Intent import Uri\n\nuri = Uri(\"file:///sdcard/test.txt\")\n
"},{"location":"qsl4a/core/intent/#usage-examples","title":"Usage Examples","text":""},{"location":"qsl4a/core/intent/#open-web-page","title":"Open Web Page","text":"
intent = droid.makeIntent(\n    action=droid.Intent.ACTION_VIEW,\n    uri=\"http://www.example.com\"\n).result\ndroid.startActivityIntent(intent)\n
"},{"location":"qsl4a/core/intent/#share-text","title":"Share Text","text":"
intent = droid.makeIntent(\n    action=droid.Intent.ACTION_SEND,\n    extras={\n        droid.Intent.EXTRA_TEXT: \"Hello from QPython!\",\n        droid.Intent.EXTRA_SUBJECT: \"Test\"\n    },\n    type=\"text/plain\"\n).result\ndroid.startActivityIntent(intent)\n
"},{"location":"qsl4a/core/intent/#open-file","title":"Open File","text":"
intent = droid.makeIntent(\n    action=droid.Intent.ACTION_VIEW,\n    uri=\"file:///sdcard/document.pdf\",\n    type=\"application/pdf\"\n).result\ndroid.startActivityIntent(intent)\n
"},{"location":"qsl4a/core/intent/#pick-contact","title":"Pick Contact","text":"
result = droid.pickContact()\ncontact_uri = result.result\n
"},{"location":"qsl4a/core/intent/#send-email","title":"Send Email","text":"
intent = droid.makeIntent(\n    action=droid.Intent.ACTION_SEND,\n    extras={\n        droid.Intent.EXTRA_EMAIL: [\"test@example.com\"],\n        droid.Intent.EXTRA_SUBJECT: \"Hello\",\n        droid.Intent.EXTRA_TEXT: \"Message body\"\n    },\n    type=\"message/rfc822\"\n).result\ndroid.startActivityIntent(intent)\n
"},{"location":"qsl4a/core/intent/#open-app","title":"Open App","text":"
intent = droid.makeIntent(\n    action=droid.Intent.ACTION_MAIN,\n    packagename=\"com.android.settings\",\n    classname=\"com.android.settings.Settings\"\n).result\ndroid.startActivityIntent(intent)\n
"},{"location":"qsl4a/hardware/bluetooth/","title":"Bluetooth API","text":"

Control Bluetooth adapter and communicate with Bluetooth devices.

"},{"location":"qsl4a/hardware/bluetooth/#adapter-control","title":"Adapter Control","text":""},{"location":"qsl4a/hardware/bluetooth/#togglebluetoothstate","title":"toggleBluetoothState()","text":"

Turn Bluetooth on/off.

toggleBluetoothState(enabled=None, prompt=True)\n

Parameters: - enabled (bool): True=on, False=off, None=toggle - prompt (bool): Show user prompt

"},{"location":"qsl4a/hardware/bluetooth/#checkbluetoothstate","title":"checkBluetoothState()","text":"

Check if Bluetooth is enabled.

checkBluetoothState()\n

Returns: True/False

"},{"location":"qsl4a/hardware/bluetooth/#getlocalname","title":"GetLocalName()","text":"

Get Bluetooth device name.

GetLocalName()\n
"},{"location":"qsl4a/hardware/bluetooth/#setlocalname","title":"SetLocalName()","text":"

Set Bluetooth device name.

SetLocalName(name)\n
"},{"location":"qsl4a/hardware/bluetooth/#getscanmode","title":"GetScanMode()","text":"

Get discoverability mode.

GetScanMode()\n

Returns: - -1: Disabled - 0: Non-discoverable, non-connectable - 1: Connectable, non-discoverable - 3: Connectable and discoverable

"},{"location":"qsl4a/hardware/bluetooth/#makediscoverable","title":"MakeDiscoverable()","text":"

Make device discoverable.

MakeDiscoverable(duration=300)\n

Parameters: - duration (int): Seconds to be discoverable

"},{"location":"qsl4a/hardware/bluetooth/#discovery","title":"Discovery","text":""},{"location":"qsl4a/hardware/bluetooth/#discoverystart","title":"DiscoveryStart()","text":"

Start device discovery.

DiscoveryStart()\n
"},{"location":"qsl4a/hardware/bluetooth/#discoverycancel","title":"DiscoveryCancel()","text":"

Cancel discovery.

DiscoveryCancel()\n
"},{"location":"qsl4a/hardware/bluetooth/#getreceiveddevices","title":"GetReceivedDevices()","text":"

Get discovered devices.

GetReceivedDevices()\n

Returns: List of device info dicts

"},{"location":"qsl4a/hardware/bluetooth/#getbondeddevices","title":"GetBondedDevices()","text":"

Get paired devices.

GetBondedDevices()\n

Returns: List of paired device info

"},{"location":"qsl4a/hardware/bluetooth/#connection","title":"Connection","text":""},{"location":"qsl4a/hardware/bluetooth/#connect","title":"Connect()","text":"

Connect to a device.

Connect(uuid=\"457807c0-4897-11df-9879-0800200c9a66\", address=None)\n

Parameters: - uuid (str): Service UUID - address (str): Device address (None = show picker)

Returns: True if successful

"},{"location":"qsl4a/hardware/bluetooth/#accept","title":"Accept()","text":"

Accept incoming connection.

Accept(uuid=\"457807c0-4897-11df-9879-0800200c9a66\", timeout=0)\n
"},{"location":"qsl4a/hardware/bluetooth/#activeconnections","title":"ActiveConnections()","text":"

Check active connections.

ActiveConnections()\n
"},{"location":"qsl4a/hardware/bluetooth/#stop","title":"Stop()","text":"

Disconnect.

Stop(connID=None)\n
"},{"location":"qsl4a/hardware/bluetooth/#communication","title":"Communication","text":""},{"location":"qsl4a/hardware/bluetooth/#write","title":"Write()","text":"

Send ASCII data.

Write(ascii, connID=\"\")\n
"},{"location":"qsl4a/hardware/bluetooth/#writebinary","title":"WriteBinary()","text":"

Send binary data (base64 encoded).

WriteBinary(base64, connID=None)\n
"},{"location":"qsl4a/hardware/bluetooth/#read","title":"Read()","text":"

Read ASCII data.

Read(bufferSize=4096, connID=None)\n
"},{"location":"qsl4a/hardware/bluetooth/#readbinary","title":"ReadBinary()","text":"

Read binary data.

ReadBinary(bufferSize=4096, connID=None)\n
"},{"location":"qsl4a/hardware/bluetooth/#readline","title":"ReadLine()","text":"

Read line.

ReadLine(connID=None)\n
"},{"location":"qsl4a/hardware/bluetooth/#readready","title":"ReadReady()","text":"

Check if data available.

ReadReady(connID=None)\n
"},{"location":"qsl4a/hardware/bluetooth/#usage-example","title":"Usage Example","text":"
import androidhelper\n\ndroid = androidhelper.Android()\n\n# Enable Bluetooth\ndroid.toggleBluetoothState(True)\n\n# Get paired devices\ndevices = droid.GetBondedDevices().result\nfor dev in devices:\n    print(f\"{dev['name']}: {dev['address']}\")\n\n# Connect to device\ndroid.Connect(address=\"00:11:22:33:44:55\")\n\n# Send data\ndroid.Write(\"Hello Bluetooth!\")\n\n# Read response\ndata = droid.Read(1024).result\n
"},{"location":"qsl4a/hardware/camera/","title":"Camera API","text":"

Capture photos and record video.

"},{"location":"qsl4a/hardware/camera/#photo-capture","title":"Photo Capture","text":""},{"location":"qsl4a/hardware/camera/#takepicture","title":"takePicture()","text":"

Take a photo using the default camera.

takePicture(path=None)\n

Parameters: - path (str, optional): Save path. If None, returns image data

Returns: Image path or image data

"},{"location":"qsl4a/hardware/camera/#cameracapturepicture","title":"cameraCapturePicture()","text":"

Capture picture with advanced camera controls.

cameraCapturePicture(targetPath=None, cameraId=0, useAutoFocus=True)\n

Parameters: - targetPath (str, optional): Save path - cameraId (int): Camera to use (0 = back, 1 = front) - useAutoFocus (bool): Enable auto focus

Returns: Captured image path

"},{"location":"qsl4a/hardware/camera/#camerasettorchmode","title":"cameraSetTorchMode()","text":"

Control camera flashlight/torch.

cameraSetTorchMode(enabled)\n

Parameters: - enabled (bool): True to turn on, False to turn off

Returns: True if successful

"},{"location":"qsl4a/hardware/camera/#imagereadergetscreenshot","title":"imageReaderGetScreenShot()","text":"

Capture screenshot.

imageReaderGetScreenShot(path=None, delayMilliSec=1000)\n

Parameters: - path (str, optional): Save path - delayMilliSec (int): Delay before capture

"},{"location":"qsl4a/hardware/camera/#video-recording","title":"Video Recording","text":""},{"location":"qsl4a/hardware/camera/#takevideo","title":"takeVideo()","text":"

Record video using default settings.

takeVideo(path=None, quality=1)\n

Parameters: - path (str, optional): Save path - quality (int): Video quality (0-4) - 0: 160x120 - 1: 320x240 - 2: 352x288 - 3: 640x480 - 4: 800x480

"},{"location":"qsl4a/hardware/camera/#recordercapturevideo","title":"recorderCaptureVideo()","text":"

Capture video with advanced controls.

recorderCaptureVideo(targetPath=None, duration=10, cameraId=0, quality=8)\n

Parameters: - targetPath (str, optional): Save path - duration (int): Recording duration in seconds (default: 10) - cameraId (int): Camera to use (0 = back, 1 = front) - quality (int): Video quality (0-8, higher is better)

Returns: Video file path

"},{"location":"qsl4a/hardware/camera/#recordaudio","title":"recordAudio()","text":"

Record audio.

recordAudio()\n

Returns: Audio file path

"},{"location":"qsl4a/hardware/camera/#screen-recording","title":"Screen Recording","text":""},{"location":"qsl4a/hardware/camera/#recorderstartscreenrecord","title":"recorderStartScreenRecord()","text":"

Start screen recording.

recorderStartScreenRecord(path=None, audio=1, targetPixels=None,\n                          frameRate=30, bitRate=None, rotation=False,\n                          autoStart=True)\n

Parameters: - path (str): Save path - audio (int): Audio source (0=none, 1=mic, 2=internal) - targetPixels (int): Resolution - frameRate (int): FPS - bitRate (int): Bitrate - rotation (bool): Rotate output - autoStart (bool): Start immediately

"},{"location":"qsl4a/hardware/camera/#recorderstart","title":"recorderStart()","text":"

Start recording.

recorderStart()\n
"},{"location":"qsl4a/hardware/camera/#recorderpause","title":"recorderPause()","text":"

Pause recording.

recorderPause()\n
"},{"location":"qsl4a/hardware/camera/#recorderresume","title":"recorderResume()","text":"

Resume recording.

recorderResume()\n
"},{"location":"qsl4a/hardware/camera/#audio-volume-detection","title":"Audio Volume Detection","text":""},{"location":"qsl4a/hardware/camera/#recordersoundvolumedetect","title":"recorderSoundVolumeDetect()","text":"

Start volume detection.

recorderSoundVolumeDetect(interval=100)\n
"},{"location":"qsl4a/hardware/camera/#recordersoundvolumegetdb","title":"recorderSoundVolumeGetDb()","text":"

Get current volume in dB.

recorderSoundVolumeGetDb()\n
"},{"location":"qsl4a/hardware/camera/#usage-example","title":"Usage Example","text":"
import androidhelper\n\ndroid = androidhelper.Android()\n\n# Take photo with default camera\nphoto_path = droid.takePicture(\"/sdcard/photo.jpg\").result\nprint(f\"Photo saved: {photo_path}\")\n\n# Take photo with front camera and auto focus\ncamera_path = droid.cameraCapturePicture(\"/sdcard/selfie.jpg\", cameraId=1, useAutoFocus=True).result\nprint(f\"Front camera photo: {camera_path}\")\n\n# Control flashlight\ndroid.cameraSetTorchMode(True)  # Turn on flashlight\n\n# Record video with default settings\nvideo_path = droid.takeVideo(\"/sdcard/video.mp4\", quality=3).result\nprint(f\"Video saved: {video_path}\")\n\n# Record video with advanced controls\nvideo_path = droid.recorderCaptureVideo(\"/sdcard/movie.mp4\", duration=30, cameraId=0, quality=8).result\n\n# Screenshot\nss_path = droid.imageReaderGetScreenShot(\"/sdcard/screenshot.png\", 500).result\n
"},{"location":"qsl4a/hardware/recorder/","title":"Audio Recorder API","text":"

Record audio from microphone and device screen.

"},{"location":"qsl4a/hardware/recorder/#audio-recording","title":"Audio Recording","text":""},{"location":"qsl4a/hardware/recorder/#recordaudio","title":"recordAudio()","text":"

Record audio from microphone.

recordAudio()\n

Returns: Path to recorded audio file

"},{"location":"qsl4a/hardware/recorder/#recorderstartmicrophone","title":"recorderStartMicrophone()","text":"

Start recording from microphone to a specific file.

recorderStartMicrophone(targetPath=None)\n

Parameters: - targetPath (str, optional): Path to save the recording

"},{"location":"qsl4a/hardware/recorder/#screen-recording","title":"Screen Recording","text":""},{"location":"qsl4a/hardware/recorder/#recorderstartscreenrecord","title":"recorderStartScreenRecord()","text":"

Start screen recording with audio.

recorderStartScreenRecord(path=None, audio=1, targetPixels=None,\n                          frameRate=30, bitRate=None, rotation=False,\n                          autoStart=True)\n

Parameters: - path (str): Save path for video file - audio (int): Audio source (0=none, 1=mic, 2=internal audio) - targetPixels (int): Target resolution in pixels - frameRate (int): Frames per second (default: 30) - bitRate (int): Video bitrate - rotation (bool): Rotate output video - autoStart (bool): Start recording immediately

Returns: Result of operation

"},{"location":"qsl4a/hardware/recorder/#recorderstart","title":"recorderStart()","text":"

Start the screen recording (when autoStart=False).

recorderStart()\n
"},{"location":"qsl4a/hardware/recorder/#recorderpause","title":"recorderPause()","text":"

Pause ongoing screen recording.

recorderPause()\n
"},{"location":"qsl4a/hardware/recorder/#recorderresume","title":"recorderResume()","text":"

Resume paused screen recording.

recorderResume()\n
"},{"location":"qsl4a/hardware/recorder/#audio-volume-detection","title":"Audio Volume Detection","text":""},{"location":"qsl4a/hardware/recorder/#recordersoundvolumedetect","title":"recorderSoundVolumeDetect()","text":"

Start monitoring sound volume level.

recorderSoundVolumeDetect(interval=100)\n

Parameters: - interval (int): Detection interval in milliseconds (default: 100)

"},{"location":"qsl4a/hardware/recorder/#recordersoundvolumegetdb","title":"recorderSoundVolumeGetDb()","text":"

Get current sound volume in decibels.

recorderSoundVolumeGetDb()\n

Returns: Current volume level in dB

"},{"location":"qsl4a/hardware/recorder/#usage-example","title":"Usage Example","text":"
import androidhelper\nimport time\n\ndroid = androidhelper.Android()\n\n# Record audio from microphone\nprint(\"Recording audio...\")\naudio_path = droid.recordAudio().result\nprint(f\"Saved to: {audio_path}\")\n\n# Record microphone to specific file\ndroid.recorderStartMicrophone(\"/sdcard/my_recording.mp3\")\ntime.sleep(5)\ndroid.recorderStop()\n\n# Record screen with audio\nprint(\"Starting screen recording...\")\ndroid.recorderStartScreenRecord(\n    path=\"/sdcard/screen_record.mp4\",\n    audio=1,\n    frameRate=30,\n    autoStart=True\n)\ntime.sleep(10)\ndroid.recorderStop()\n\n# Monitor sound volume\ndroid.recorderSoundVolumeDetect(interval=100)\ntime.sleep(3)\nvolume = droid.recorderSoundVolumeGetDb().result\nprint(f\"Current volume: {volume} dB\")\n
"},{"location":"qsl4a/hardware/usbserial/","title":"USB Host Serial API","text":"

Communicate with USB serial devices (requires USB OTG support and Android 3.1+).

"},{"location":"qsl4a/hardware/usbserial/#usb-serial-methods","title":"USB Serial Methods)","text":""},{"location":"qsl4a/hardware/usbserial/#usbhostserialopen","title":"usbHostSerialOpen()","text":"

Open a connection to a USB serial device.

usbHostSerialOpen(device, baudRate=9600)\n

Parameters: - device (str): USB device path or identifier - baudRate (int): Baud rate (default: 9600)

Returns: True if opened successfully

"},{"location":"qsl4a/hardware/usbserial/#usbhostserialclose","title":"usbHostSerialClose()","text":"

Close the USB serial connection.

usbHostSerialClose()\n
"},{"location":"qsl4a/hardware/usbserial/#usbhostserialread","title":"usbHostSerialRead()","text":"

Read data from USB serial.

usbHostSerialRead(bufferSize=1024)\n

Parameters: - bufferSize (int): Maximum bytes to read (default: 1024)

Returns: String read data

"},{"location":"qsl4a/hardware/usbserial/#usbhostserialwrite","title":"usbHostSerialWrite()","text":"

Write data to USB serial.

usbHostSerialWrite(data)\n

Parameters: - data (str): String data to write

"},{"location":"qsl4a/hardware/usbserial/#usbhostserialavailable","title":"usbHostSerialAvailable()","text":"

Check if data is available to read.

usbHostSerialAvailable()\n

Returns: Number of bytes available

"},{"location":"qsl4a/hardware/usbserial/#configuration-methods","title":"Configuration Methods)","text":""},{"location":"qsl4a/hardware/usbserial/#usbhostserialsetbaudrate","title":"usbHostSerialSetBaudRate()","text":"

Set the baud rate.

usbHostSerialSetBaudRate(baudRate)\n

Parameters: - baudRate (int): Baud rate

"},{"location":"qsl4a/hardware/usbserial/#usbhostserialsetdatabits","title":"usbHostSerialSetDataBits()","text":"

Set data bits (5, 6, 7, or 8).

usbHostSerialSetDataBits(dataBits)\n

Parameters: - dataBits (int): Data bits (5-8)

"},{"location":"qsl4a/hardware/usbserial/#usbhostserialsetstopbits","title":"usbHostSerialSetStopBits()","text":"

Set stop bits (1, 1.5, or 2).

usbHostSerialSetStopBits(stopBits)\n

Parameters: - stopBits (float): Stop bits (1, 1.5, or 2)

"},{"location":"qsl4a/hardware/usbserial/#usbhostserialsetparity","title":"usbHostSerialSetParity()","text":"

Set parity (none, odd, even, mark, space).

usbHostSerialSetParity(parity)\n

Parameters: - parity (str): Parity mode ('none', 'odd', 'even', 'mark', 'space')

"},{"location":"qsl4a/hardware/usbserial/#usbhostserialsetflowcontrol","title":"usbHostSerialSetFlowControl()","text":"

Set flow control (none, hardware, software).

usbHostSerialSetFlowControl(flowControl)\n

Parameters: - flowControl (str): Flow control mode ('none', 'hardware', 'software')

"},{"location":"qsl4a/hardware/usbserial/#usbhostserialreadhex","title":"usbHostSerialReadHex()","text":"

Read data as hex string.

usbHostSerialReadHex(bufferSize=1024)\n

Parameters: - bufferSize (int): Maximum bytes to read

Returns: Hex string

"},{"location":"qsl4a/hardware/usbserial/#usbhostserialwritehex","title":"usbHostSerialWriteHex()","text":"

Write data from hex string.

usbHostSerialWriteHex(hexString)\n

Parameters: - hexString (str): Hex string to write

"},{"location":"qsl4a/hardware/usbserial/#usage-example","title":"Usage Example","text":"
import androidhelper\n\ndroid = androidhelper.Android()\n\n# Open USB serial connection\nif droid.usbHostSerialOpen(\"/dev/bus/usb/001/001\", 115200).result:\n    print(\"USB serial opened\")\n\n    # Write data\n    droid.usbHostSerialWrite(\"AT\\r\")\n\n    # Read response\n    response = droid.usbHostSerialRead(1024).result\n    print(f\"Response: {response}\")\n\n    # Or use hex\n    droid.usbHostSerialWriteHex(\"41540D0A\")  # \"AT\\r\\n\"\n\n    # Close connection\n    droid.usbHostSerialClose()\n

Note: USB serial requires: - Android 3.1+ (API 12) - USB OTG cable/adapter - USB host mode support on device - Compatible serial device

"},{"location":"qsl4a/hardware/webcam/","title":"Webcam API","text":"

Stream video from the device camera using MJPEG.

"},{"location":"qsl4a/hardware/webcam/#mjpeg-stream-methods","title":"MJPEG Stream Methods","text":""},{"location":"qsl4a/hardware/webcam/#webcamstart","title":"webcamStart()","text":"

Start an MJPEG stream from the webcam.

webcamStart(resolutionLevel=0, jpegQuality=20, port=0)\n

Parameters: - resolutionLevel (int): Resolution level (default: 0) - jpegQuality (int): JPEG quality 1-100 (default: 20) - port (int): Port number (default: 0 = auto)

Returns: Tuple of (address, port) for the stream

"},{"location":"qsl4a/hardware/webcam/#webcamadjustquality","title":"webcamAdjustQuality()","text":"

Adjust the quality of an active webcam stream.

webcamAdjustQuality(resolutionLevel=0, jpegQuality=20)\n

Parameters: - resolutionLevel (int): Resolution level - jpegQuality (int): JPEG quality 1-100

"},{"location":"qsl4a/hardware/webcam/#webcamstop","title":"webcamStop()","text":"

Stop the webcam stream.

webcamStop()\n
"},{"location":"qsl4a/hardware/webcam/#camera-preview-methods","title":"Camera Preview Methods","text":""},{"location":"qsl4a/hardware/webcam/#camerastartpreview","title":"cameraStartPreview()","text":"

Start camera preview mode with event generation.

cameraStartPreview(resolutionLevel=0, jpegQuality=20, filepath=None)\n

Parameters: - resolutionLevel (int): Resolution level (default: 0) - jpegQuality (int): JPEG quality (default: 20) - filepath (str, optional): File path to save preview frames

Returns: True if successful

Note: Generates 'preview' events with frame data.

"},{"location":"qsl4a/hardware/webcam/#camerastoppreview","title":"cameraStopPreview()","text":"

Stop the camera preview.

cameraStopPreview()\n
"},{"location":"qsl4a/hardware/webcam/#usage-example","title":"Usage Example","text":"
import androidhelper\nimport time\n\ndroid = androidhelper.Android()\n\n# Start webcam stream\nstream_info = droid.webcamStart(\n    resolutionLevel=0,\n    jpegQuality=30,\n    port=8080\n).result\nprint(f\"Stream available at {stream_info[0]}:{stream_info[1]}\")\n\n# Adjust quality while streaming\ntime.sleep(5)\ndroid.webcamAdjustQuality(resolutionLevel=1, jpegQuality=50)\n\n# Stop when done\ndroid.webcamStop()\n\n# Or use preview mode\nprint(\"Starting preview...\")\ndroid.cameraStartPreview()\n\n# Wait for preview events\nfor i in range(10):\n    event = droid.eventWait(timeout=1).result\n    if event and event['name'] == 'preview':\n        print(f\"Got preview frame: {event['data']}\")\n\ndroid.cameraStopPreview()\n
"},{"location":"qsl4a/media/image/","title":"Image Processing API","text":"

Compress and process images.

"},{"location":"qsl4a/media/image/#image-compression","title":"Image Compression","text":""},{"location":"qsl4a/media/image/#imagecompress","title":"imageCompress()","text":"

Compress image file.

imageCompress(srcPath, destPath, targetByteSize=0, targetWidth=0, targetHeight=0)\n

Parameters: - srcPath (str): Source image path - destPath (str): Output path - targetByteSize (int): Target file size in bytes (0 = no limit) - targetWidth (int): Target width (0 = original) - targetHeight (int): Target height (0 = original)

Returns: Compressed image path

"},{"location":"qsl4a/media/image/#screenshot","title":"Screenshot","text":""},{"location":"qsl4a/media/image/#imagereadergetscreenshot","title":"imageReaderGetScreenShot()","text":"

Capture screen.

imageReaderGetScreenShot(path=None, delayMilliSec=1000)\n

Parameters: - path (str): Save path - delayMilliSec (int): Delay before capture

Returns: Screenshot path

"},{"location":"qsl4a/media/image/#video-playback","title":"Video Playback","text":""},{"location":"qsl4a/media/image/#videoplay","title":"videoPlay()","text":"

Play video file in fullscreen mode.

videoPlay(path, wait=True)\n

Parameters: - path (str): Video file path - wait (bool): Wait for playback to complete (default: True)

"},{"location":"qsl4a/media/image/#barcode-scanning","title":"Barcode Scanning","text":""},{"location":"qsl4a/media/image/#scanbarcodefromimage","title":"scanBarcodeFromImage()","text":"

Scan barcode/QR code from image file.

scanBarcodeFromImage(path, compressRatio=0, x=0, y=0, width=0, height=0)\n

Parameters: - path (str): Image file path - compressRatio (int): Compression ratio (0 = no compression) - x, y, width, height (int): Region to scan (0 = full image)

Returns: Scanned barcode content

"},{"location":"qsl4a/media/image/#usage-example","title":"Usage Example","text":"
import androidhelper\n\ndroid = androidhelper.Android()\n\n# Take screenshot\nss_path = droid.imageReaderGetScreenShot(\"/sdcard/screenshot.png\", 500).result\n\n# Compress image\ncompressed = droid.imageCompress(\n    \"/sdcard/large_photo.jpg\",\n    \"/sdcard/compressed.jpg\",\n    targetByteSize=102400,  # Target ~100KB\n    targetWidth=1920,\n    targetHeight=1080\n).result\nprint(f\"Saved: {compressed}\")\n\n# Play video\nvideo_path = \"/sdcard/movie.mp4\"\ndroid.videoPlay(video_path, wait=True)\n\n# Scan barcode from image\nresult = droid.scanBarcodeFromImage(\"/sdcard/qr_code.png\").result\nprint(f\"Barcode: {result}\")\n
"},{"location":"qsl4a/media/mediaplayer/","title":"Media Player API","text":"

Control audio and video playback.

"},{"location":"qsl4a/media/mediaplayer/#playback-control","title":"Playback Control","text":""},{"location":"qsl4a/media/mediaplayer/#mediaplay","title":"mediaPlay()","text":"

Play media file.

mediaPlay(url, tag=\"default\", play=True)\n

Parameters: - url (str): Media file path or URL - tag (str): Player identifier - play (bool): Auto-start playback

"},{"location":"qsl4a/media/mediaplayer/#mediaplaystart","title":"mediaPlayStart()","text":"

Start playback.

mediaPlayStart(tag=\"default\")\n
"},{"location":"qsl4a/media/mediaplayer/#mediaplaypause","title":"mediaPlayPause()","text":"

Pause playback.

mediaPlayPause(tag=\"default\")\n
"},{"location":"qsl4a/media/mediaplayer/#mediaplayclose","title":"mediaPlayClose()","text":"

Close player.

mediaPlayClose(tag=\"default\")\n
"},{"location":"qsl4a/media/mediaplayer/#mediaplayseek","title":"mediaPlaySeek()","text":"

Seek to position.

mediaPlaySeek(msec, tag=\"default\")\n

Parameters: - msec (int): Position in milliseconds

"},{"location":"qsl4a/media/mediaplayer/#mediaplaysetlooping","title":"mediaPlaySetLooping()","text":"

Set loop mode.

mediaPlaySetLooping(enabled, tag=\"default\")\n
"},{"location":"qsl4a/media/mediaplayer/#player-info","title":"Player Info","text":""},{"location":"qsl4a/media/mediaplayer/#mediaplayinfo","title":"mediaPlayInfo()","text":"

Get playback info.

mediaPlayInfo(tag=\"default\")\n

Returns: Dict with duration, position, etc.

"},{"location":"qsl4a/media/mediaplayer/#mediaisplaying","title":"mediaIsPlaying()","text":"

Check if playing.

mediaIsPlaying(tag=\"default\")\n

Returns: True/False

"},{"location":"qsl4a/media/mediaplayer/#mediaplaylist","title":"mediaPlayList()","text":"

List active players.

mediaPlayList()\n
"},{"location":"qsl4a/media/mediaplayer/#volume-control","title":"Volume Control","text":""},{"location":"qsl4a/media/mediaplayer/#getmediavolume","title":"getMediaVolume()","text":"

Get media volume.

getMediaVolume()\n

Returns: Volume level (0-15)

"},{"location":"qsl4a/media/mediaplayer/#getmaxmediavolume","title":"getMaxMediaVolume()","text":"

Get max media volume.

getMaxMediaVolume()\n
"},{"location":"qsl4a/media/mediaplayer/#getringervolume","title":"getRingerVolume()","text":"

Get ringer volume.

getRingerVolume()\n
"},{"location":"qsl4a/media/mediaplayer/#getmaxringervolume","title":"getMaxRingerVolume()","text":"

Get max ringer volume.

getMaxRingerVolume()\n
"},{"location":"qsl4a/media/mediaplayer/#video-playback","title":"Video Playback","text":""},{"location":"qsl4a/media/mediaplayer/#videoplay","title":"videoPlay()","text":"

Play video fullscreen.

videoPlay(path, wait=True)\n

Parameters: - path (str): Video file path - wait (bool): Wait for playback to complete

"},{"location":"qsl4a/media/mediaplayer/#usage-example","title":"Usage Example","text":"
import androidhelper\n\ndroid = androidhelper.Android()\n\n# Play audio\ndroid.mediaPlay(\"/sdcard/music.mp3\", tag=\"music\")\n\n# Check status\nif droid.mediaIsPlaying(\"music\").result:\n    info = droid.mediaPlayInfo(\"music\").result\n    print(f\"Playing: {info}\")\n\n# Seek to 30 seconds\ndroid.mediaPlaySeek(30000, \"music\")\n\n# Close\ndroid.mediaPlayClose(\"music\")\n\n# Play video\ndroid.videoPlay(\"/sdcard/movie.mp4\", wait=True)\n
"},{"location":"qsl4a/special/cipher/","title":"Cipher API","text":"

Encryption and decryption utilities for secure data storage.

"},{"location":"qsl4a/special/cipher/#initialization","title":"Initialization","text":""},{"location":"qsl4a/special/cipher/#cipherinit","title":"cipherInit()","text":"

Initialize the cipher with encryption key and algorithm.

cipherInit(key, algorithm=\"AES/CBC/PKCS5Padding\", encodingFormat=\"\", initialVector=\"\")\n

Parameters: - key (str or bytes): Encryption key - algorithm (str): Cipher algorithm (default: \"AES/CBC/PKCS5Padding\") - encodingFormat (str): Encoding format - initialVector (str or bytes): Initial vector for encryption

Returns: Initialization result

Note: Must be called before any encrypt/decrypt operations.

"},{"location":"qsl4a/special/cipher/#encryption-methods","title":"Encryption Methods","text":""},{"location":"qsl4a/special/cipher/#encryptstring","title":"encryptString()","text":"

Encrypt a string.

encryptString(plainText)\n

Parameters: - plainText (str): Text to encrypt

Returns: Encrypted string

"},{"location":"qsl4a/special/cipher/#encryptbytes","title":"encryptBytes()","text":"

Encrypt bytes data.

encryptBytes(data)\n

Parameters: - data (bytes): Data to encrypt

Returns: Encrypted bytes

"},{"location":"qsl4a/special/cipher/#encryptstringtofile","title":"encryptStringToFile()","text":"

Encrypt string to file.

encryptStringToFile(plainText, filePath)\n

Parameters: - plainText (str): Text to encrypt - filePath (str): Output file path

"},{"location":"qsl4a/special/cipher/#encryptbytestofile","title":"encryptBytesToFile()","text":"

Encrypt bytes to file.

encryptBytesToFile(data, filePath)\n
"},{"location":"qsl4a/special/cipher/#decryption-methods","title":"Decryption Methods","text":""},{"location":"qsl4a/special/cipher/#decryptstring","title":"decryptString()","text":"

Decrypt to string.

decryptString(cipherText)\n

Parameters: - cipherText (str): Encrypted text

Returns: Decrypted string

"},{"location":"qsl4a/special/cipher/#decryptbytes","title":"decryptBytes()","text":"

Decrypt to bytes.

decryptBytes(data)\n

Returns: Decrypted bytes

"},{"location":"qsl4a/special/cipher/#decryptfiletostring","title":"decryptFileToString()","text":"

Decrypt file to string.

decryptFileToString(filePath)\n
"},{"location":"qsl4a/special/cipher/#decryptfiletobytes","title":"decryptFileToBytes()","text":"

Decrypt file to bytes.

decryptFileToBytes(filePath)\n
"},{"location":"qsl4a/special/cipher/#decryptfile","title":"decryptFile()","text":"

Decrypt file to file.

decryptFile(srcPath, destPath)\n
"},{"location":"qsl4a/special/cipher/#usage-example","title":"Usage Example","text":"
import androidhelper\n\ndroid = androidhelper.Android()\n\n# Initialize cipher with your encryption key\ndroid.cipherInit(\"my_secret_key_1234\")\n\n# Encrypt string\nencrypted = droid.encryptString(\"Secret message!\").result\nprint(f\"Encrypted: {encrypted}\")\n\n# Decrypt string\ndecrypted = droid.decryptString(encrypted).result\nprint(f\"Decrypted: {decrypted}\")\n\n# Encrypt to file\ndroid.encryptStringToFile(\"My secret data\", \"/sdcard/secret.dat\")\n\n# Decrypt from file\ndata = droid.decryptFileToString(\"/sdcard/secret.dat\").result\nprint(data)\n\n# Initialize with custom algorithm and IV\ndroid.cipherInit(\n    key=\"my_key\",\n    algorithm=\"AES/CBC/PKCS5Padding\",\n    initialVector=\"0123456789abcdef\"\n)\n
"},{"location":"qsl4a/special/pgptai/","title":"PGPT AI API","text":"

Speech-to-text and AI services integration.

"},{"location":"qsl4a/special/pgptai/#prerequisites","title":"Prerequisites","text":"
pip install pgptAI\n
"},{"location":"qsl4a/special/pgptai/#speech-recognition","title":"Speech Recognition","text":""},{"location":"qsl4a/special/pgptai/#speechtotext","title":"speechToText()","text":"

Convert speech to text.

speechToText(RecordSecond=10, AmrFile=None, Language=None)\n

Parameters: - RecordSecond (int): Recording duration in seconds - AmrFile (str, optional): Existing audio file path - Language (str, optional): Language code ('en', 'zh')

Returns: Transcribed text

"},{"location":"qsl4a/special/pgptai/#text-to-speech","title":"Text to Speech","text":""},{"location":"qsl4a/special/pgptai/#texttospeech","title":"textToSpeech()","text":"

Convert text to speech and optionally play it.

textToSpeech(Text, AutoPlay=True, WavFile=None, VoiceName=None)\n

Parameters: - Text (str): Text to convert to speech - AutoPlay (bool): Automatically play the generated audio (default: True) - WavFile (str, optional): Path to save the WAV file. If None, uses temporary file - VoiceName (str, optional): Voice name to use (e.g., 'en-US-JennyNeural', 'zh-CN-XiaoxiaoNeural')

Returns: Dict with speech synthesis result including: - text: The input text - url: URL to download the audio file - WavFile: Path to the saved WAV file (if saved locally)

"},{"location":"qsl4a/special/pgptai/#configuration","title":"Configuration","text":"

The API uses configuration from /storage/emulated/0/Android/data/org.qpython.qpy/files/apigpt.conf:

[speech]\nspeech_key = your_api_key\n

Default voice settings: - English: en-US-JennyNeural - Chinese: zh-CN-XiaoxiaoNeural

"},{"location":"qsl4a/special/pgptai/#usage-example","title":"Usage Example","text":"
import androidhelper\n\ndroid = androidhelper.Android()\n\n# Record and transcribe\nprint(\"Recording for 5 seconds...\")\ntext = droid.speechToText(RecordSecond=5, Language='en').result\nprint(f\"You said: {text}\")\n\n# Transcribe existing file\ntext = droid.speechToText(AmrFile=\"/sdcard/recording.amr\").result\nprint(f\"Transcription: {text}\")\n\n# Text to speech\ndroid.textToSpeech(\"Hello, this is a test message.\", AutoPlay=True, Language='en')\n\n# Text to speech with custom voice and save to file\nresult = droid.textToSpeech(\n    \"Welcome to QPython!\",\n    AutoPlay=False,\n    WavFile=\"/sdcard/welcome.wav\",\n    VoiceName=\"en-US-JennyNeural\"\n).result\nprint(f\"Audio saved to: {result.get('WavFile')}\")\n
"},{"location":"qsl4a/special/pgptai/#class-usage","title":"Class Usage","text":"
from androidhelper.pgptai import pgptai\nimport androidhelper\n\ndroid = androidhelper.Android()\nai = pgptai(droid)\n\n# Use speech recognition\ntext = ai.speechToText(RecordSecond=10)\n
"},{"location":"qsl4a/storage/clipboard/","title":"Clipboard API","text":"

Copy and paste text to system clipboard.

"},{"location":"qsl4a/storage/clipboard/#methods","title":"Methods","text":""},{"location":"qsl4a/storage/clipboard/#setclipboard","title":"setClipboard()","text":"

Copy text to clipboard.

setClipboard(text)\n

Parameters: - text (str): Text to copy

Returns: True if success

"},{"location":"qsl4a/storage/clipboard/#getclipboard","title":"getClipboard()","text":"

Get text from clipboard.

getClipboard()\n

Returns: Clipboard text

"},{"location":"qsl4a/storage/clipboard/#usage-example","title":"Usage Example","text":"
import androidhelper\n\ndroid = androidhelper.Android()\n\n# Copy to clipboard\ndroid.setClipboard(\"Hello from QPython!\")\n\n# Paste from clipboard\ntext = droid.getClipboard().result\nprint(f\"Clipboard: {text}\")\n
"},{"location":"qsl4a/storage/documentfile/","title":"DocumentFile API","text":"

File operations with SAF (Storage Access Framework) support for Android 4.4+.

"},{"location":"qsl4a/storage/documentfile/#directory-operations","title":"Directory Operations","text":""},{"location":"qsl4a/storage/documentfile/#documentfilemkdir","title":"documentFileMkdir()","text":"

Create directory.

documentFileMkdir(Dir)\n

Parameters: - Dir (str): Directory path

Returns: True if success

"},{"location":"qsl4a/storage/documentfile/#documentfilelistfiles","title":"documentFileListFiles()","text":"

List files in directory.

documentFileListFiles(Folder)\n

Returns: List of files

"},{"location":"qsl4a/storage/documentfile/#file-operations","title":"File Operations","text":""},{"location":"qsl4a/storage/documentfile/#documentfileexists","title":"documentFileExists()","text":"

Check if file or directory exists.

documentFileExists(path)\n

Parameters: - path (str): File or directory path

Returns: True if exists, False otherwise

"},{"location":"qsl4a/storage/documentfile/#documentfileisfile","title":"documentFileIsFile()","text":"

Check if path is a file.

documentFileIsFile(path)\n

Parameters: - path (str): Path to check

Returns: True if file, False if not a file, None if not exists

"},{"location":"qsl4a/storage/documentfile/#documentfileisdirectory","title":"documentFileIsDirectory()","text":"

Check if path is a directory.

documentFileIsDirectory(path)\n

Parameters: - path (str): Path to check

Returns: True if directory, False if not a directory, None if not exists

"},{"location":"qsl4a/storage/documentfile/#documentfiledelete","title":"documentFileDelete()","text":"

Delete file or directory.

documentFileDelete(FileOrTree)\n

Returns: True if success

"},{"location":"qsl4a/storage/documentfile/#documentfilerenameto","title":"documentFileRenameTo()","text":"

Rename or move file.

documentFileRenameTo(Src, Dest)\n

Returns: True if success

"},{"location":"qsl4a/storage/documentfile/#documentfilecopy","title":"documentFileCopy()","text":"

Copy file.

documentFileCopy(SrcFileOrTree, DestFileOrTree)\n
"},{"location":"qsl4a/storage/documentfile/#stream-operations","title":"Stream Operations","text":""},{"location":"qsl4a/storage/documentfile/#documentfileinputstream","title":"documentFileInputStream()","text":"

Read file content.

documentFileInputStream(srcFile, EncodingFormat=\"\", skip=None, length=None)\n

Parameters: - srcFile (str): Source file - EncodingFormat (str): \"UTF-8\", \"GBK\", \"Base64\", or \"\" for bytes - skip (int): Skip bytes from start - length (int): Read length

Returns: File content

"},{"location":"qsl4a/storage/documentfile/#documentfileoutputstream","title":"documentFileOutputStream()","text":"

Write file content.

documentFileOutputStream(destFile, src, EncodingFormat=\"\", append=None)\n

Parameters: - destFile (str): Destination file - src: Data to write - EncodingFormat (str): Encoding format - append (bool): Append mode

"},{"location":"qsl4a/storage/documentfile/#file-information","title":"File Information","text":""},{"location":"qsl4a/storage/documentfile/#documentfilelength","title":"documentFileLength()","text":"

Get file size in bytes.

documentFileLength(path)\n

Parameters: - path (str): File path

Returns: File size in bytes (0 if not exists)

"},{"location":"qsl4a/storage/documentfile/#documentfilelastmodified","title":"documentFileLastModified()","text":"

Get last modified time.

documentFileLastModified(path)\n

Parameters: - path (str): File path

Returns: Timestamp (0 if not exists)

"},{"location":"qsl4a/storage/documentfile/#documentfilegetstat","title":"documentFileGetStat()","text":"

Get comprehensive file statistics.

documentFileGetStat(path)\n

Parameters: - path (str): File path

Returns: Dict with length, last modified, and read/write permissions, or None if not exists

"},{"location":"qsl4a/storage/documentfile/#uri-operations","title":"URI Operations","text":""},{"location":"qsl4a/storage/documentfile/#documentfilegeturi","title":"documentFileGetUri()","text":"

Get URI from path.

documentFileGetUri(path, isDirectory=None)\n
"},{"location":"qsl4a/storage/documentfile/#documentfileshowopen","title":"documentFileShowOpen()","text":"

Show file picker.

documentFileShowOpen()\n

Returns: Selected file URI

"},{"location":"qsl4a/storage/documentfile/#usage-example","title":"Usage Example","text":"
import androidhelper\n\ndroid = androidhelper.Android()\n\n# Create directory\ndroid.documentFileMkdir(\"/sdcard/MyFolder\")\n\n# List files\nfiles = droid.documentFileListFiles(\"/sdcard\").result\nfor f in files:\n    print(f)\n\n# Read file\ncontent = droid.documentFileInputStream(\n    \"/sdcard/test.txt\",\n    EncodingFormat=\"UTF-8\"\n).result\nprint(content)\n\n# Write file\ndroid.documentFileOutputStream(\n    \"/sdcard/output.txt\",\n    \"Hello World\",\n    EncodingFormat=\"UTF-8\"\n)\n
"},{"location":"qsl4a/storage/preferences/","title":"Preferences API","text":"

Store and retrieve data using Android SharedPreferences.

"},{"location":"qsl4a/storage/preferences/#preference-methods","title":"Preference Methods","text":""},{"location":"qsl4a/storage/preferences/#prefgetvalue","title":"prefGetValue()","text":"

Read a value from shared preferences.

prefGetValue(key, filename=None)\n

Parameters: - key (str): Preference key - filename (str, optional): Preference file name

Returns: The stored value (any type)

"},{"location":"qsl4a/storage/preferences/#prefputvalue","title":"prefPutValue()","text":"

Write a value to shared preferences.

prefPutValue(key, value, filename=None)\n

Parameters: - key (str): Preference key - value (object): Value to store - filename (str, optional): Preference file name

"},{"location":"qsl4a/storage/preferences/#prefgetall","title":"prefGetAll()","text":"

Get all preference values.

prefGetAll(filename=None)\n

Parameters: - filename (str, optional): Preference file name

Returns: Map of all preferences

"},{"location":"qsl4a/storage/preferences/#prefremovevalue","title":"prefRemoveValue()","text":"

Remove a value from shared preferences.

prefRemoveValue(key, filename=None)\n

Parameters: - key (str): Preference key to remove - filename (str, optional): Preference file name

"},{"location":"qsl4a/storage/preferences/#usage-example","title":"Usage Example","text":"
import androidhelper\n\ndroid = androidhelper.Android()\n\n# Store values\ndroid.prefPutValue(\"username\", \"alice\")\ndroid.prefPutValue(\"score\", 100)\ndroid.prefPutValue(\"enabled\", True)\n\n# Read a specific value\nusername = droid.prefGetValue(\"username\").result\nprint(f\"Username: {username}\")\n\n# Get all preferences\nall_prefs = droid.prefGetAll().result\nprint(f\"All prefs: {all_prefs}\")\n\n# Remove a value\ndroid.prefRemoveValue(\"score\")\n\n# Use custom filename\ndroid.prefPutValue(\"token\", \"abc123\", filename=\"auth.prefs\")\ntoken = droid.prefGetValue(\"token\", filename=\"auth.prefs\").result\n
"},{"location":"qsl4a/system/activityresult/","title":"Activity Result API","text":"

Set activity results for scripts launched via startActivityForResult.

"},{"location":"qsl4a/system/activityresult/#result-methods","title":"Result Methods","text":""},{"location":"qsl4a/system/activityresult/#setresultboolean","title":"setResultBoolean()","text":"

Set a boolean result.

setResultBoolean(resultCode, resultValue)\n

Parameters: - resultCode (int): Result code - resultValue (bool): Boolean result value

"},{"location":"qsl4a/system/activityresult/#setresultbyte","title":"setResultByte()","text":"

Set a byte result.

setResultByte(resultCode, resultValue)\n

Parameters: - resultCode (int): Result code - resultValue (int): Byte result value

"},{"location":"qsl4a/system/activityresult/#setresultshort","title":"setResultShort()","text":"

Set a short result.

setResultShort(resultCode, resultValue)\n

Parameters: - resultCode (int): Result code - resultValue (int): Short result value

"},{"location":"qsl4a/system/activityresult/#setresultchar","title":"setResultChar()","text":"

Set a character result.

setResultChar(resultCode, resultValue)\n

Parameters: - resultCode (int): Result code - resultValue (str): Character result value

"},{"location":"qsl4a/system/activityresult/#setresultinteger","title":"setResultInteger()","text":"

Set an integer result.

setResultInteger(resultCode, resultValue)\n

Parameters: - resultCode (int): Result code - resultValue (int): Integer result value

"},{"location":"qsl4a/system/activityresult/#setresultlong","title":"setResultLong()","text":"

Set a long result.

setResultLong(resultCode, resultValue)\n

Parameters: - resultCode (int): Result code - resultValue (int): Long result value

"},{"location":"qsl4a/system/activityresult/#setresultfloat","title":"setResultFloat()","text":"

Set a float result.

setResultFloat(resultCode, resultValue)\n

Parameters: - resultCode (int): Result code - resultValue (float): Float result value

"},{"location":"qsl4a/system/activityresult/#setresultdouble","title":"setResultDouble()","text":"

Set a double result.

setResultDouble(resultCode, resultValue)\n

Parameters: - resultCode (int): Result code - resultValue (float): Double result value

"},{"location":"qsl4a/system/activityresult/#setresultstring","title":"setResultString()","text":"

Set a string result.

setResultString(resultCode, resultValue)\n

Parameters: - resultCode (int): Result code - resultValue (str): String result value

"},{"location":"qsl4a/system/activityresult/#setresultbooleanarray","title":"setResultBooleanArray()","text":"

Set a boolean array result.

setResultBooleanArray(resultCode, resultValue)\n

Parameters: - resultCode (int): Result code - resultValue (list): Boolean array

"},{"location":"qsl4a/system/activityresult/#setresultbytearray","title":"setResultByteArray()","text":"

Set a byte array result.

setResultByteArray(resultCode, resultValue)\n

Parameters: - resultCode (int): Result code - resultValue (list): Byte array

"},{"location":"qsl4a/system/activityresult/#setresultshortarray","title":"setResultShortArray()","text":"

Set a short array result.

setResultShortArray(resultCode, resultValue)\n

Parameters: - resultCode (int): Result code - resultValue (list): Short array

"},{"location":"qsl4a/system/activityresult/#setresultchararray","title":"setResultCharArray()","text":"

Set a character array result.

setResultCharArray(resultCode, resultValue)\n

Parameters: - resultCode (int): Result code - resultValue (list): Char array

"},{"location":"qsl4a/system/activityresult/#setresultintegerarray","title":"setResultIntegerArray()","text":"

Set an integer array result.

setResultIntegerArray(resultCode, resultValue)\n

Parameters: - resultCode (int): Result code - resultValue (list): Integer array

"},{"location":"qsl4a/system/activityresult/#setresultlongarray","title":"setResultLongArray()","text":"

Set a long array result.

setResultLongArray(resultCode, resultValue)\n

Parameters: - resultCode (int): Result code - resultValue (list): Long array

"},{"location":"qsl4a/system/activityresult/#setresultfloatarray","title":"setResultFloatArray()","text":"

Set a float array result.

setResultFloatArray(resultCode, resultValue)\n

Parameters: - resultCode (int): Result code - resultValue (list): Float array

"},{"location":"qsl4a/system/activityresult/#setresultdoublearray","title":"setResultDoubleArray()","text":"

Set a double array result.

setResultDoubleArray(resultCode, resultValue)\n

Parameters: - resultCode (int): Result code - resultValue (list): Double array

"},{"location":"qsl4a/system/activityresult/#setresultstringarray","title":"setResultStringArray()","text":"

Set a string array result.

setResultStringArray(resultCode, resultValue)\n

Parameters: - resultCode (int): Result code - resultValue (list): String array

"},{"location":"qsl4a/system/activityresult/#setresultserializable","title":"setResultSerializable()","text":"

Set a serializable result.

setResultSerializable(resultCode, resultValue)\n

Parameters: - resultCode (int): Result code - resultValue: Serializable result value

"},{"location":"qsl4a/system/activityresult/#usage-example","title":"Usage Example","text":"
import androidhelper\n\ndroid = androidhelper.Android()\n\n# After performing an activity, set the result\n# Example: Return success with data\ndroid.setResultInteger(0, 200)  # RESULT_OK\ndroid.setResultString(0, \"Operation completed successfully\")\n\n# Return an array result\ndroid.setResultIntegerArray(0, [1, 2, 3, 4, 5])\n
"},{"location":"qsl4a/system/application/","title":"Application Management","text":"

Manage applications, launch apps, and query system information.

"},{"location":"qsl4a/system/application/#application-information","title":"Application Information","text":""},{"location":"qsl4a/system/application/#getapplicationinfo","title":"getApplicationInfo()","text":"

Get information about an app.

getApplicationInfo(packageName=None)\n

Parameters: - packageName (str): Package name (None = current app)

Returns: App info dict

"},{"location":"qsl4a/system/application/#getinstalledpackages","title":"getInstalledPackages()","text":"

Get list of installed packages.

getInstalledPackages(flag=4)\n

Parameters: - flag (int): Package flag filter (default: 4)

Returns: List of installed packages

"},{"location":"qsl4a/system/application/#getrunningpackages","title":"getRunningPackages()","text":"

List running packages.

getRunningPackages()\n

Returns: List of package names

"},{"location":"qsl4a/system/application/#getlaunchablepackages","title":"getLaunchablePackages()","text":"

Get list of launchable packages.

getLaunchablePackages(needClassName=False)\n

Parameters: - needClassName (bool): Include main activity class name (default: False)

Returns: List of launchable package names or dict with class names

"},{"location":"qsl4a/system/application/#application-control","title":"Application Control","text":""},{"location":"qsl4a/system/application/#launch","title":"launch()","text":"

Launch an application.

launch(classname=None, packagename=None, wait=True)\n

Parameters: - classname (str): Main activity class name - packagename (str): Package name - wait (bool): Wait for launch to complete (default: True)

Returns: Launch result

"},{"location":"qsl4a/system/application/#forcestoppackage","title":"forceStopPackage()","text":"

Force stop an application.

forceStopPackage(packageName)\n

Parameters: - packageName (str): Package name to stop

"},{"location":"qsl4a/system/application/#version-information","title":"Version Information","text":""},{"location":"qsl4a/system/application/#getpackageversion","title":"getPackageVersion()","text":"

Get app version name.

getPackageVersion(packageName)\n

Returns: Version string (e.g., \"3.2.1\")

"},{"location":"qsl4a/system/application/#getpackageversioncode","title":"getPackageVersionCode()","text":"

Get app version code.

getPackageVersionCode(packageName)\n

Returns: Version code integer

"},{"location":"qsl4a/system/application/#getconstants","title":"getConstants()","text":"

Get class constants.

getConstants(classname)\n

Parameters: - classname (str): Full class name

Returns: Dict of constant names and values

"},{"location":"qsl4a/system/application/#system-features","title":"System Features","text":""},{"location":"qsl4a/system/application/#backgroundprotect","title":"backgroundProtect()","text":"

Enable or disable background protection for the app.

backgroundProtect(enabled=True)\n

Parameters: - enabled (bool): True to enable protection, False to disable

"},{"location":"qsl4a/system/application/#createscriptshortcut","title":"createScriptShortCut()","text":"

Create a home screen shortcut for a script.

createScriptShortCut(scriptPath, label=None, iconPath=None, scriptArg=None)\n

Parameters: - scriptPath (str): Path to the Python script - label (str, optional): Shortcut label - iconPath (str, optional): Path to icon image - scriptArg (str, optional): Argument to pass to script

"},{"location":"qsl4a/system/application/#device-information","title":"Device Information","text":""},{"location":"qsl4a/system/application/#getandroidid","title":"getAndroidID()","text":"

Get Android device ID.

getAndroidID()\n

Returns: Unique Android device ID string

"},{"location":"qsl4a/system/application/#getsysinfo","title":"getSysInfo()","text":"

Get system information.

getSysInfo()\n

Returns: Dict with system details

"},{"location":"qsl4a/system/application/#getlocale","title":"getLocale()","text":"

Get device locale.

getLocale()\n

Returns: Locale string (e.g., \"en_US\")

"},{"location":"qsl4a/system/application/#getharmonyosinformation","title":"getHarmonyOsInformation()","text":"

Get HarmonyOS information if running on HarmonyOS.

getHarmonyOsInformation()\n

Returns: HarmonyOS version info or None

"},{"location":"qsl4a/system/application/#isexternalstoragemanager","title":"isExternalStorageManager()","text":"

Check if app has external storage manager permission.

isExternalStorageManager()\n

Returns: True if has permission

"},{"location":"qsl4a/system/application/#memory-and-display","title":"Memory and Display","text":""},{"location":"qsl4a/system/application/#getmemoryinfo","title":"getMemoryInfo()","text":"

Get memory information.

getMemoryInfo()\n

Returns: Dict with memory stats

"},{"location":"qsl4a/system/application/#getscreeninfo","title":"getScreenInfo()","text":"

Get screen information.

getScreenInfo()\n

Returns: Dict with width, height, density

"},{"location":"qsl4a/system/application/#permissions","title":"Permissions","text":""},{"location":"qsl4a/system/application/#checkpermissions","title":"checkPermissions()","text":"

Check current app permissions.

checkPermissions()\n

Returns: Dict of permissions and their status

"},{"location":"qsl4a/system/application/#requestpermissions","title":"requestPermissions()","text":"

Request permissions from the user.

requestPermissions(permissions=None)\n

Parameters: - permissions (list, optional): List of permissions to request. If None, requests all needed permissions.

Returns: Result of permission request

"},{"location":"qsl4a/system/application/#system-actions","title":"System Actions","text":""},{"location":"qsl4a/system/application/#showscreenlock","title":"showScreenLock()","text":"

Show screen lock (PIN/pattern/password entry).

showScreenLock()\n
"},{"location":"qsl4a/system/application/#usage-example","title":"Usage Example","text":"
import androidhelper\n\ndroid = androidhelper.Android()\n\n# Get system info\ninfo = droid.getSysInfo().result\nprint(f\"System: {info}\")\n\n# Get app version\nversion = droid.getPackageVersion(\"org.qpython.qpy\").result\nprint(f\"QPython version: {version}\")\n\n# List installed apps\napps = droid.getInstalledPackages().result\nprint(f\"Installed apps: {len(apps)}\")\n\n# Launch an app\ndroid.launch(packagename=\"com.android.settings\")\n\n# Check permissions\nperms = droid.checkPermissions().result\nprint(f\"Permissions: {perms}\")\n\n# Request storage permission\ndroid.requestPermissions([\"android.permission.READ_EXTERNAL_STORAGE\"])\n\n# Create shortcut\ndroid.createScriptShortCut(\n    \"/sdcard/my_script.py\",\n    label=\"My Script\",\n    iconPath=\"/sdcard/icon.png\"\n)\n
"},{"location":"qsl4a/system/battery/","title":"Battery API","text":"

Monitor device battery status and health.

"},{"location":"qsl4a/system/battery/#methods","title":"Methods","text":""},{"location":"qsl4a/system/battery/#readbatterydata","title":"readBatteryData()","text":"

Get complete battery information.

readBatteryData()\n

Returns: Dict with battery data

"},{"location":"qsl4a/system/battery/#batterystartmonitoring","title":"batteryStartMonitoring()","text":"

Start battery monitoring.

batteryStartMonitoring()\n
"},{"location":"qsl4a/system/battery/#batterystopmonitoring","title":"batteryStopMonitoring()","text":"

Stop battery monitoring.

batteryStopMonitoring()\n
"},{"location":"qsl4a/system/battery/#batterygetlevel","title":"batteryGetLevel()","text":"

Get battery percentage.

batteryGetLevel()\n

Returns: Int (0-100)

"},{"location":"qsl4a/system/battery/#batterygetstatus","title":"batteryGetStatus()","text":"

Get charging status.

batteryGetStatus()\n

Returns: - 1: Unknown - 2: Charging - 3: Discharging - 4: Not charging - 5: Full

"},{"location":"qsl4a/system/battery/#batterygetplugtype","title":"batteryGetPlugType()","text":"

Get power source.

batteryGetPlugType()\n

Returns: - -1: Unknown - 0: Unplugged - 1: AC charger - 2: USB port

"},{"location":"qsl4a/system/battery/#batterygethealth","title":"batteryGetHealth()","text":"

Get battery health.

batteryGetHealth()\n

Returns: - 1: Unknown - 2: Good - 3: Overheat - 4: Dead - 5: Over voltage - 6: Unspecified failure

"},{"location":"qsl4a/system/battery/#usage-example","title":"Usage Example","text":"
import androidhelper\n\ndroid = androidhelper.Android()\n\n# Get battery level\nlevel = droid.batteryGetLevel().result\nprint(f\"Battery: {level}%\")\n
"},{"location":"qsl4a/system/qpyinterface/","title":"QPython Interface API","text":"

Execute QPython scripts and manage shared variables from other apps.

"},{"location":"qsl4a/system/qpyinterface/#script-execution-methods","title":"Script Execution Methods","text":""},{"location":"qsl4a/system/qpyinterface/#executeqpy","title":"executeQPy()","text":"

Execute a QPython script.

executeQPy(path=\"\", arg=None)\n

Parameters: - path (str): Path to the script file - arg (str, optional): Command line arguments

Returns: True if started successfully

"},{"location":"qsl4a/system/qpyinterface/#executeqpyassrv","title":"executeQPyAsSrv()","text":"

Execute a QPython script as a service.

executeQPyAsSrv(path=None)\n

Parameters: - path (str, optional): Path to the script file

Returns: True if started successfully

"},{"location":"qsl4a/system/qpyinterface/#executeqpycode","title":"executeQPyCode()","text":"

Execute Python code directly.

executeQPyCode(code=None)\n

Parameters: - code (str, optional): Python code to execute

Returns: True if started successfully

"},{"location":"qsl4a/system/qpyinterface/#executeqpycodeassrv","title":"executeQPyCodeAsSrv()","text":"

Execute Python code as a service.

executeQPyCodeAsSrv(code=None)\n

Parameters: - code (str, optional): Python code to execute

Returns: True if started successfully

"},{"location":"qsl4a/system/qpyinterface/#shared-variables","title":"Shared Variables","text":"

Shared variables allow communication between QPython and other apps.

"},{"location":"qsl4a/system/qpyinterface/#sharedvariableset","title":"sharedVariableSet()","text":"

Set a Java shared variable.

sharedVariableSet(key, value)\n

Parameters: - key (str): Variable name - value (str): Variable value

Returns: The stored value

"},{"location":"qsl4a/system/qpyinterface/#sharedvariableget","title":"sharedVariableGet()","text":"

Get a Java shared variable.

sharedVariableGet(key)\n

Parameters: - key (str): Variable name

Returns: The stored value

"},{"location":"qsl4a/system/qpyinterface/#sharedvariableremove","title":"sharedVariableRemove()","text":"

Remove a Java shared variable.

sharedVariableRemove(key)\n

Parameters: - key (str): Variable name to remove

Returns: The removed value

"},{"location":"qsl4a/system/qpyinterface/#getlastlog","title":"getLastLog()","text":"

Get the last log output from QPython.

getLastLog()\n

Returns: String log content

"},{"location":"qsl4a/system/qpyinterface/#usage-example","title":"Usage Example","text":"
import androidhelper\n\ndroid = androidhelper.Android()\n\n# Execute a script\ndroid.executeQPy(\"/sdcard/my_script.py\", arg=\"test\")\n\n# Execute code directly\ncode = \"print('Hello from QPython!')\"\ndroid.executeQPyCode(code)\n\n# Use shared variables\ndroid.sharedVariableSet(\"username\", \"alice\")\nusername = droid.sharedVariableGet(\"username\").result\nprint(f\"Username: {username}\")\n\n# Remove variable\ndroid.sharedVariableRemove(\"username\")\n\n# Get recent log\nlog = droid.getLastLog().result\nprint(f\"Log: {log}\")\n
"},{"location":"qsl4a/system/sensors/","title":"Sensor API","text":"

Access device sensors including accelerometer, gyroscope, magnetometer, and more.

"},{"location":"qsl4a/system/sensors/#methods","title":"Methods","text":""},{"location":"qsl4a/system/sensors/#startsensingtimed","title":"startSensingTimed()","text":"

Start sensor monitoring with time intervals.

startSensingTimed(sensorNumber, delayTime)\n

Parameters: - sensorNumber (int): Sensor ID (1-3 typically) - delayTime (int): Delay between readings in milliseconds

"},{"location":"qsl4a/system/sensors/#startsensingthreshold","title":"startSensingThreshold()","text":"

Start sensor monitoring with threshold trigger.

startSensingThreshold(sensorNumber, threshold, axis)\n

Parameters: - sensorNumber (int): Sensor ID - threshold (float): Trigger threshold - axis (int): Axis to monitor (0=X, 1=Y, 2=Z)

"},{"location":"qsl4a/system/sensors/#stopsensing","title":"stopSensing()","text":"

Stop all sensor monitoring.

stopSensing()\n
"},{"location":"qsl4a/system/sensors/#readsensors","title":"readSensors()","text":"

Read current sensor data.

readSensors()\n

Returns: Sensor data dict

"},{"location":"qsl4a/system/sensors/#sensorsreadaccelerometer","title":"sensorsReadAccelerometer()","text":"

Read accelerometer values.

sensorsReadAccelerometer()\n

Returns: List [X, Y, Z] in m/s\u00b2

"},{"location":"qsl4a/system/sensors/#sensorsreadgyroscope","title":"sensorsReadGyroscope()","text":"

Read gyroscope values.

sensorsReadGyroscope()\n

Returns: List [X, Y, Z] in rad/s

"},{"location":"qsl4a/system/sensors/#sensorsreadmagnetometer","title":"sensorsReadMagnetometer()","text":"

Read magnetic field values.

sensorsReadMagnetometer()\n

Returns: List [X, Y, Z] in \u03bcT

"},{"location":"qsl4a/system/sensors/#sensorsreadorientation","title":"sensorsReadOrientation()","text":"

Read device orientation.

sensorsReadOrientation()\n

Returns: List [azimuth, pitch, roll] in degrees

"},{"location":"qsl4a/system/sensors/#sensorsgetlight","title":"sensorsGetLight()","text":"

Read light sensor value.

sensorsGetLight()\n

Returns: Light level in lux

"},{"location":"qsl4a/system/sensors/#sensorsgetstepcounter","title":"sensorsGetStepCounter()","text":"

Read step counter.

sensorsGetStepCounter()\n

Returns: Number of steps

"},{"location":"qsl4a/system/sensors/#sensorsgetaccuracy","title":"sensorsGetAccuracy()","text":"

Get the current sensor accuracy.

sensorsGetAccuracy()\n

Returns: Accuracy value (0-3: UNRELIABLE, ACCURACY_LOW, ACCURACY_MEDIUM, ACCURACY_HIGH)

"},{"location":"qsl4a/system/sensors/#usage-example","title":"Usage Example","text":"
import androidhelper\nimport time\n\ndroid = androidhelper.Android()\n\n# Start sensing\ndroid.startSensingTimed(1, 250)\n\n# Read sensors 10 times\nfor i in range(10):\n    accel = droid.sensorsReadAccelerometer().result\n    print(f\"Accel: X={accel[0]:.2f}, Y={accel[1]:.2f}, Z={accel[2]:.2f}\")\n    time.sleep(0.5)\n\ndroid.stopSensing()\n
"},{"location":"qsl4a/system/settings/","title":"Settings API","text":"

Control system settings including screen, sound, and network settings.

"},{"location":"qsl4a/system/settings/#screen-settings","title":"Screen Settings","text":""},{"location":"qsl4a/system/settings/#setscreentimeout","title":"setScreenTimeout()","text":"

Set the screen timeout value.

setScreenTimeout(value)\n

Parameters: - value (int): Screen timeout in seconds

Returns: Previous timeout value

"},{"location":"qsl4a/system/settings/#getscreentimeout","title":"getScreenTimeout()","text":"

Get the current screen timeout.

getScreenTimeout()\n

Returns: Current screen timeout in seconds

"},{"location":"qsl4a/system/settings/#getscreenbrightness","title":"getScreenBrightness()","text":"

Get the screen brightness value.

getScreenBrightness()\n

Returns: Brightness value (0-255)

"},{"location":"qsl4a/system/settings/#setscreenbrightness","title":"setScreenBrightness()","text":"

Set the screen brightness.

setScreenBrightness(value=None)\n

Parameters: - value (int, optional): Brightness value (0-255), or None for auto

Returns: Previous brightness value

"},{"location":"qsl4a/system/settings/#checkscreenon","title":"checkScreenOn()","text":"

Check if the screen is on.

checkScreenOn()\n

Returns: True if screen is on, False otherwise

"},{"location":"qsl4a/system/settings/#airplane-mode","title":"Airplane Mode","text":""},{"location":"qsl4a/system/settings/#checkairplanemode","title":"checkAirplaneMode()","text":"

Check if airplane mode is enabled.

checkAirplaneMode()\n

Returns: True if airplane mode is on

"},{"location":"qsl4a/system/settings/#ringer-settings","title":"Ringer Settings","text":""},{"location":"qsl4a/system/settings/#checkringersilentmode","title":"checkRingerSilentMode()","text":"

Check if ringer is in silent mode.

checkRingerSilentMode()\n

Returns: True if silent mode is on

"},{"location":"qsl4a/system/settings/#toggleringersilentmode","title":"toggleRingerSilentMode()","text":"

Toggle ringer silent mode.

toggleRingerSilentMode(enabled=None)\n

Parameters: - enabled (bool, optional): True to enable, False to disable, None to toggle

Returns: New state

"},{"location":"qsl4a/system/settings/#togglevibratemode","title":"toggleVibrateMode()","text":"

Toggle vibrate mode.

toggleVibrateMode(enabled=None, ringer=None)\n

Parameters: - enabled (bool, optional): Toggle vibrate on/off - ringer (bool, optional): Apply to ringer mode

Returns: New state

"},{"location":"qsl4a/system/settings/#getvibratemode","title":"getVibrateMode()","text":"

Get the vibrate mode setting.

getVibrateMode(ringer=None)\n

Parameters: - ringer (bool, optional): Check ringer vibrate mode

Returns: True if vibrate is enabled

"},{"location":"qsl4a/system/settings/#volume-settings","title":"Volume Settings","text":""},{"location":"qsl4a/system/settings/#getringervolume","title":"getRingerVolume()","text":"

Get the current ringer volume.

getRingerVolume()\n

Returns: Ringer volume level (0-7 typically)

"},{"location":"qsl4a/system/settings/#getmaxringervolume","title":"getMaxRingerVolume()","text":"

Get the maximum ringer volume.

getMaxRingerVolume()\n

Returns: Maximum ringer volume

"},{"location":"qsl4a/system/settings/#setringervolume","title":"setRingerVolume()","text":"

Set the ringer volume.

setRingerVolume(volume)\n

Parameters: - volume (int): Volume level

"},{"location":"qsl4a/system/settings/#getmediavolume","title":"getMediaVolume()","text":"

Get the current media volume.

getMediaVolume()\n

Returns: Media volume level (0-15 typically)

"},{"location":"qsl4a/system/settings/#getmaxmediavolume","title":"getMaxMediaVolume()","text":"

Get the maximum media volume.

getMaxMediaVolume()\n

Returns: Maximum media volume

"},{"location":"qsl4a/system/settings/#setmediavolume","title":"setMediaVolume()","text":"

Set the media volume.

setMediaVolume(volume)\n

Parameters: - volume (int): Volume level

"},{"location":"qsl4a/system/settings/#system-info","title":"System Info","text":""},{"location":"qsl4a/system/settings/#elapsedrealtimenanos","title":"elapsedRealtimeNanos()","text":"

Get nanoseconds since system startup.

elapsedRealtimeNanos()\n

Returns: Nanoseconds (can be used for timing)

"},{"location":"qsl4a/system/settings/#gettrafficstats","title":"getTrafficStats()","text":"

Get network traffic statistics.

getTrafficStats(flags=7)\n

Parameters: - flags (int): Which stats to retrieve (default: 7 = all)

Returns: Dict with transmit/receive bytes

"},{"location":"qsl4a/system/settings/#getapptxbytes","title":"getAppTxBytes()","text":"

Get transmit bytes for QPython app.

getAppTxBytes(packageName)\n

Parameters: - packageName (str): Package name

Returns: Dict with tx/rx bytes

"},{"location":"qsl4a/system/settings/#usage-example","title":"Usage Example","text":"
import androidhelper\n\ndroid = androidhelper.Android()\n\n# Screen settings\ncurrent_timeout = droid.getScreenTimeout().result\nprint(f\"Current timeout: {current_timeout}s\")\ndroid.setScreenTimeout(30)\n\n# Check screen\nif droid.checkScreenOn().result:\n    print(\"Screen is on\")\n\n# Volume control\nmedia_vol = droid.getMediaVolume().result\nprint(f\"Media volume: {media_vol}\")\ndroid.setMediaVolume(10)\n\n# Check airplane mode\nif droid.checkAirplaneMode().result:\n    print(\"Airplane mode is on\")\n
"},{"location":"qsl4a/system/sysinfo/","title":"System Information","text":"

Retrieve device and system information.

"},{"location":"qsl4a/system/sysinfo/#device-information","title":"Device Information","text":""},{"location":"qsl4a/system/sysinfo/#getandroidid","title":"getAndroidID()","text":"

Get the Android device ID.

getAndroidID()\n

Returns: String device ID

"},{"location":"qsl4a/system/sysinfo/#getsysinfo","title":"getSysInfo()","text":"

Get comprehensive system information.

getSysInfo()\n

Returns: Dict with system details

"},{"location":"qsl4a/system/sysinfo/#getlocale","title":"getLocale()","text":"

Get device locale settings.

getLocale()\n

Returns: Locale string (e.g., \"en_US\")

"},{"location":"qsl4a/system/sysinfo/#memory","title":"Memory","text":""},{"location":"qsl4a/system/sysinfo/#getmemoryinfo","title":"getMemoryInfo()","text":"

Get RAM information.

getMemoryInfo()\n

Returns: Dict with memory stats

"},{"location":"qsl4a/system/sysinfo/#display","title":"Display","text":""},{"location":"qsl4a/system/sysinfo/#getscreeninfo","title":"getScreenInfo()","text":"

Get display information.

getScreenInfo()\n

Returns: Dict with width, height, density

"},{"location":"qsl4a/system/sysinfo/#identifiers","title":"Identifiers","text":""},{"location":"qsl4a/system/sysinfo/#getimei","title":"getImei()","text":"

Get device IMEI.

getImei(slotIndex=None)\n
"},{"location":"qsl4a/system/sysinfo/#getmeid","title":"getMeid()","text":"

Get device MEID.

getMeid(slotIndex=None)\n
"},{"location":"qsl4a/system/sysinfo/#usage-example","title":"Usage Example","text":"
import androidhelper\n\ndroid = androidhelper.Android()\n\n# Device ID\nandroid_id = droid.getAndroidID().result\nprint(f\"Android ID: {android_id}\")\n\n# Screen info\nscreen = droid.getScreenInfo().result\nprint(f\"Screen: {screen['width']}x{screen['height']}\")\n\n# Memory\nmemory = droid.getMemoryInfo().result\nprint(f\"Memory: {memory}\")\n
"},{"location":"qsl4a/system/wakelock/","title":"WakeLock API","text":"

Control device wake locks to keep the CPU or screen on.

"},{"location":"qsl4a/system/wakelock/#wake-lock-types","title":"Wake Lock Types","text":"

QSL4A provides different wake lock types:

Type Description Full CPU on, screen bright, keyboard bright Partial CPU on only Bright CPU on, screen bright Dim CPU on, screen dim"},{"location":"qsl4a/system/wakelock/#wake-lock-methods","title":"Wake Lock Methods","text":""},{"location":"qsl4a/system/wakelock/#wakelockacquirefull","title":"wakeLockAcquireFull()","text":"

Acquire a full wake lock (CPU on, screen bright, keyboard bright).

wakeLockAcquireFull()\n
"},{"location":"qsl4a/system/wakelock/#wakelockacquirepartial","title":"wakeLockAcquirePartial()","text":"

Acquire a partial wake lock (CPU on only).

wakeLockAcquirePartial()\n
"},{"location":"qsl4a/system/wakelock/#wakelockacquirebright","title":"wakeLockAcquireBright()","text":"

Acquire a bright wake lock (CPU on, screen bright).

wakeLockAcquireBright()\n
"},{"location":"qsl4a/system/wakelock/#wakelockacquiredim","title":"wakeLockAcquireDim()","text":"

Acquire a dim wake lock (CPU on, screen dim).

wakeLockAcquireDim()\n
"},{"location":"qsl4a/system/wakelock/#wakelockrelease","title":"wakeLockRelease()","text":"

Release the wake lock.

wakeLockRelease()\n
"},{"location":"qsl4a/system/wakelock/#usage-example","title":"Usage Example","text":"
import androidhelper\nimport time\n\ndroid = androidhelper.Android()\n\n# Acquire full wake lock\ndroid.wakeLockAcquireFull()\n\n# Do important work while keeping screen on\nprint(\"Screen will stay on\")\ntime.sleep(10)\n\n# Release when done\ndroid.wakeLockRelease()\n\n# Or use partial lock for background tasks\ndroid.wakeLockAcquirePartial()\n# CPU stays on even with screen off\ntime.sleep(30)\ndroid.wakeLockRelease()\n

Note: Remember to release wake locks when no longer needed to conserve battery.

"},{"location":"qsl4a/ui/accessibility/","title":"Accessibility Service","text":"

The Accessibility Service allows automation of UI interactions like clicks, swipes, and system actions.

"},{"location":"qsl4a/ui/accessibility/#service-control","title":"Service Control","text":""},{"location":"qsl4a/ui/accessibility/#accessibilitystartservice","title":"accessibilityStartService()","text":"

Start the accessibility service.

accessibilityStartService()\n

Returns: True if successful, False otherwise

"},{"location":"qsl4a/ui/accessibility/#accessibilityserviceenabled","title":"accessibilityServiceEnabled()","text":"

Check if accessibility service is enabled.

accessibilityServiceEnabled()\n

Returns: True or False

"},{"location":"qsl4a/ui/accessibility/#screen-interactions","title":"Screen Interactions","text":""},{"location":"qsl4a/ui/accessibility/#accessibilityclick","title":"accessibilityClick()","text":"

Click at screen coordinates.

accessibilityClick(x=0, y=0, t=50)\n

Parameters: - x (int/float): X coordinate (0=center, decimals supported) - y (int/float): Y coordinate (0=center, decimals supported) - t (int): Press duration in milliseconds (default: 50)

"},{"location":"qsl4a/ui/accessibility/#accessibilityslide","title":"accessibilitySlide()","text":"

Multi-point slide gesture.

accessibilitySlide(XnYn=None, t=None)\n

Parameters: - XnYn (list): Coordinates [X1, Y1, X2, Y2, ... Xn, Yn] - t (int): Slide duration (default: 50*n ms)

"},{"location":"qsl4a/ui/accessibility/#system-actions","title":"System Actions","text":""},{"location":"qsl4a/ui/accessibility/#accessibilityaction","title":"accessibilityAction()","text":"

Execute system action by code.

accessibilityAction(actionCode)\n

Action Codes: | Code | Constant | Description | |------|----------|-------------| | 1 | BACK | Back button | | 2 | HOME | Home button | | 3 | RECENTS | Recent apps | | 4 | NOTIFICATIONS | Open notifications | | 5 | QUICK_SETTINGS | Open quick settings | | 6 | POWER_DIALOG | Power menu | | 7 | TOGGLE_SPLIT_SCREEN | Split screen | | 8 | LOCK_SCREEN | Lock screen | | 9 | TAKE_SCREENSHOT | Take screenshot | | 10 | KEYCODE_HEADSETHOOK | Headset hook | | 11-14 | ACCESSIBILITY_ | Accessibility buttons | | 15 | DISMISS_NOTIFICATION_SHADE | Close notifications | | 16-20 | DPAD_ | Directional pad | | 21 | MENU | Menu button | | 22 | MEDIA_PLAY_PAUSE | Play/Pause media |

"},{"location":"qsl4a/ui/accessibility/#usage-examples","title":"Usage Examples","text":""},{"location":"qsl4a/ui/accessibility/#click-center-screen","title":"Click Center Screen","text":"
import androidhelper\n\ndroid = androidhelper.Android()\n\n# Start service\nif not droid.accessibilityServiceEnabled().result:\n    droid.accessibilityStartService()\n\n# Click center (0,0 = center)\ndroid.accessibilityClick(0, 0, t=100)\n
"},{"location":"qsl4a/ui/accessibility/#swipe-gesture","title":"Swipe Gesture","text":"
# Swipe from bottom to top (scroll up)\ndroid.accessibilitySlide([0, 1.5, 0, -1.5], t=300)\n
"},{"location":"qsl4a/ui/accessibility/#system-navigation","title":"System Navigation","text":"
# Press home\ndroid.accessibilityAction(2)\n\n# Press back\ndroid.accessibilityAction(1)\n\n# Open notifications\ndroid.accessibilityAction(4)\n
"},{"location":"qsl4a/ui/accessibility/#tap-specific-location","title":"Tap Specific Location","text":"
# Tap at screen coordinate (500, 800)\ndroid.accessibilityClick(500, 800)\n\n# Tap at relative position (center horizontally, 3/4 down)\ndroid.accessibilityClick(0, 1.5)\n
"},{"location":"qsl4a/ui/dialogs/","title":"Dialog System","text":"

QSL4A provides comprehensive dialog support for user interaction, including alerts, input dialogs, choice dialogs, and progress dialogs.

"},{"location":"qsl4a/ui/dialogs/#alert-dialogs","title":"Alert Dialogs","text":""},{"location":"qsl4a/ui/dialogs/#dialogshowalert","title":"dialogShowAlert()","text":"

Show a simple alert dialog.

dialogShowAlert(title=\"Alert\", message=\"The message of the alert.\",\n                positiveButtonText=\"OK\", negativeButtonText=None,\n                neutralButtonText=None, messageIsHtml=False)\n

Parameters: - title (str): Dialog title - message (str): Message text - positiveButtonText (str): Positive button label - negativeButtonText (str, optional): Negative button label - neutralButtonText (str, optional): Neutral button label - messageIsHtml (bool): Parse message as HTML

Returns: Result with button clicked

"},{"location":"qsl4a/ui/dialogs/#dialogshowsimplechoice","title":"dialogShowSimpleChoice()","text":"

Show a simple choice dialog with items.

dialogShowSimpleChoice(title=\"Alert\", message=\"The message of the alert.\",\n                       items=None, positiveButtonText=\"OK\",\n                       negativeButtonText=None, neutralButtonText=None,\n                       messageIsHtml=False)\n

Parameters: - items (list): List of choice strings

Returns: Result with selected item

"},{"location":"qsl4a/ui/dialogs/#input-dialogs","title":"Input Dialogs","text":""},{"location":"qsl4a/ui/dialogs/#dialoggetinput","title":"dialogGetInput()","text":"

Get text input from user.

dialogGetInput(title=\"Value\", message=\"Please enter value:\",\n               defaultText=None, messageIsHtml=False)\n

Returns: Result with user's input text

"},{"location":"qsl4a/ui/dialogs/#dialoggetpassword","title":"dialogGetPassword()","text":"

Get password input.

dialogGetPassword(title=\"Password\", message=\"Please enter password:\")\n

Returns: Result with password entered

"},{"location":"qsl4a/ui/dialogs/#dialogcreateinput","title":"dialogCreateInput()","text":"

Create a custom input dialog.

dialogCreateInput(title=\"Value\", message=\"Please enter value:\",\n                  defaultText=None, inputType=None)\n

Parameters: - inputType (str): Input type (e.g., \"text\", \"number\", \"textPassword\")

"},{"location":"qsl4a/ui/dialogs/#dialogcreatepassword","title":"dialogCreatePassword()","text":"

Create a password input dialog.

dialogCreatePassword(title=\"Password\", message=\"Please enter password:\")\n
"},{"location":"qsl4a/ui/dialogs/#dialogcreateseekbar","title":"dialogCreateSeekBar()","text":"

Create a seek bar/slider dialog.

dialogCreateSeekBar(starting_value=50, maximum_value=100, title=\"\", message=\"\")\n

Parameters: - starting_value (int): Initial value - maximum_value (int): Maximum value

"},{"location":"qsl4a/ui/dialogs/#choice-dialogs","title":"Choice Dialogs","text":""},{"location":"qsl4a/ui/dialogs/#dialogshowsinglechoice","title":"dialogShowSingleChoice()","text":"

Show single choice (radio) dialog.

dialogShowSingleChoice(title=\"Alert\", message=\"The message of the alert.\",\n                       items=None, selected=-1, positiveButtonText=\"OK\",\n                       negativeButtonText=None, neutralButtonText=None,\n                       messageIsHtml=False)\n

Parameters: - items (list): List of choice strings - selected (int): Default selected index

"},{"location":"qsl4a/ui/dialogs/#dialogshowmultichoice","title":"dialogShowMultiChoice()","text":"

Show multiple choice (checkbox) dialog.

dialogShowMultiChoice(title=\"Alert\", message=\"The message of the alert.\",\n                      items=None, selected=None, positiveButtonText=\"OK\",\n                      negativeButtonText=None, neutralButtonText=None,\n                      messageIsHtml=False)\n

Parameters: - items (list): List of choice strings - selected (list): List of initially selected indices

"},{"location":"qsl4a/ui/dialogs/#dialogsetsinglechoiceitems","title":"dialogSetSingleChoiceItems()","text":"

Set single choice items for a dialog.

dialogSetSingleChoiceItems(items, selected=-1)\n
"},{"location":"qsl4a/ui/dialogs/#dialogsetmultichoiceitems","title":"dialogSetMultiChoiceItems()","text":"

Set multiple choice items for a dialog.

dialogSetMultiChoiceItems(items, selected=None)\n
"},{"location":"qsl4a/ui/dialogs/#progress-dialogs","title":"Progress Dialogs","text":""},{"location":"qsl4a/ui/dialogs/#dialogcreatespinnerprogress","title":"dialogCreateSpinnerProgress()","text":"

Create indeterminate progress dialog.

dialogCreateSpinnerProgress(title=None, message=None, maximum_progress=100)\n
"},{"location":"qsl4a/ui/dialogs/#dialogcreatehorizontalprogress","title":"dialogCreateHorizontalProgress()","text":"

Create horizontal progress dialog.

dialogCreateHorizontalProgress(title=None, message=None, maximum_progress=100)\n
"},{"location":"qsl4a/ui/dialogs/#dialogsetcurrentprogress","title":"dialogSetCurrentProgress()","text":"

Update progress value.

dialogSetCurrentProgress(current)\n
"},{"location":"qsl4a/ui/dialogs/#dialogsetmaxprogress","title":"dialogSetMaxProgress()","text":"

Set maximum progress value.

dialogSetMaxProgress(max)\n
"},{"location":"qsl4a/ui/dialogs/#dialogsetprogressmessage","title":"dialogSetProgressMessage()","text":"

Update the progress dialog message.

dialogSetProgressMessage(message)\n
"},{"location":"qsl4a/ui/dialogs/#picker-dialogs","title":"Picker Dialogs","text":""},{"location":"qsl4a/ui/dialogs/#dialogcreatedatepicker","title":"dialogCreateDatePicker()","text":"

Create date picker dialog.

dialogCreateDatePicker(year=1970, month=1, day=1)\n
"},{"location":"qsl4a/ui/dialogs/#dialogcreatetimepicker","title":"dialogCreateTimePicker()","text":"

Create time picker dialog.

dialogCreateTimePicker(hour=0, minute=0, is24hour=False)\n
"},{"location":"qsl4a/ui/dialogs/#custom-dialog-creation","title":"Custom Dialog Creation","text":""},{"location":"qsl4a/ui/dialogs/#dialogcreatealert","title":"dialogCreateAlert()","text":"

Create a custom alert dialog.

dialogCreateAlert(title=None, message=None)\n

Creates an empty alert dialog. Use with other dialogSet* functions to customize.

"},{"location":"qsl4a/ui/dialogs/#dialogsetitems","title":"dialogSetItems()","text":"

Set simple list items for the dialog.

dialogSetItems(items)\n
"},{"location":"qsl4a/ui/dialogs/#dialogsetpositivebuttontext","title":"dialogSetPositiveButtonText()","text":"

Set positive button text.

dialogSetPositiveButtonText(text)\n
"},{"location":"qsl4a/ui/dialogs/#dialogsetnegativebuttontext","title":"dialogSetNegativeButtonText()","text":"

Set negative button text.

dialogSetNegativeButtonText(text)\n
"},{"location":"qsl4a/ui/dialogs/#dialogsetneutralbuttontext","title":"dialogSetNeutralButtonText()","text":"

Set neutral button text.

dialogSetNeutralButtonText(text)\n
"},{"location":"qsl4a/ui/dialogs/#dialogsetmessageishtml","title":"dialogSetMessageIsHtml()","text":"

Set whether message should be parsed as HTML.

dialogSetMessageIsHtml(messageIsHtml=True)\n
"},{"location":"qsl4a/ui/dialogs/#dialogshow","title":"dialogShow()","text":"

Show the created custom dialog.

dialogShow()\n
"},{"location":"qsl4a/ui/dialogs/#dialog-control","title":"Dialog Control","text":""},{"location":"qsl4a/ui/dialogs/#dialogdismiss","title":"dialogDismiss()","text":"

Dismiss current dialog.

dialogDismiss()\n
"},{"location":"qsl4a/ui/dialogs/#dialoggetresponse","title":"dialogGetResponse()","text":"

Get dialog response.

dialogGetResponse()\n
"},{"location":"qsl4a/ui/dialogs/#dialoggetselecteditems","title":"dialogGetSelectedItems()","text":"

Get selected items from choice dialog.

dialogGetSelectedItems()\n
"},{"location":"qsl4a/ui/dialogs/#usage-examples","title":"Usage Examples","text":""},{"location":"qsl4a/ui/dialogs/#simple-alert","title":"Simple Alert","text":"
import androidhelper\n\ndroid = androidhelper.Android()\n\n# Show alert\ndroid.dialogShowAlert(\"Warning\", \"This is an important message!\")\n
"},{"location":"qsl4a/ui/dialogs/#input-dialog","title":"Input Dialog","text":"
# Get user input\nresult = droid.dialogGetInput(\"Name\", \"Enter your name:\", \"John\").result\nprint(f\"Hello, {result}!\")\n
"},{"location":"qsl4a/ui/dialogs/#custom-dialog-with-buttons","title":"Custom Dialog with Buttons","text":"
# Create custom dialog\ndroid.dialogCreateAlert(\"Custom\", \"Choose an option\")\ndroid.dialogSetItems([\"Option 1\", \"Option 2\", \"Option 3\"])\ndroid.dialogShow()\n\n# Get response\nresponse = droid.dialogGetResponse().result\nprint(f\"Selected: {response['item']}\")\n
"},{"location":"qsl4a/ui/dialogs/#progress-dialog","title":"Progress Dialog","text":"
# Create progress dialog\ndroid.dialogCreateHorizontalProgress(\"Loading\", \"Please wait...\", 100)\ndroid.dialogShow()\n\n# Update progress\nfor i in range(101):\n    droid.dialogSetCurrentProgress(i)\n    time.sleep(0.05)\n\ndroid.dialogDismiss()\n
"},{"location":"qsl4a/ui/dialogs/#date-picker","title":"Date Picker","text":"
# Show date picker\ndroid.dialogCreateDatePicker(2024, 1, 15)\ndroid.dialogShow()\nresponse = droid.dialogGetResponse().result\nprint(f\"Selected: {response}\")\n
"},{"location":"qsl4a/ui/floatview/","title":"FloatView","text":"

Floating window support for overlay UI elements that stay on top of other applications.

"},{"location":"qsl4a/ui/floatview/#methods","title":"Methods","text":""},{"location":"qsl4a/ui/floatview/#floatview_1","title":"floatView()","text":"

Show or modify a floating view.

floatView(Args=None)\n

Parameters: - Args (dict): Configuration dictionary with the following keys: - index (int): Float view index (-1 to create new, >=0 to modify existing) - text (str): Text content to display - html (str): HTML content (used if text is omitted) - width (int): View width in pixels (default: 300) - height (int): View height in pixels (default: 150) - x (int): X position (0 = center, positive/negative for offset) - y (int): Y position (0 = center, positive/negative for offset) - backColor (str): Background color in ARGB hex (default: '7f7f7f7f') - textColor (str): Text color in ARGB hex (default: 'ff000000') - textSize (int): Text size (default: 10) - textAlign (int): Text alignment (0 = inherit) - script (str): Script path to run after long click close - arg: Script argument - clickRemove (bool): Enable click to remove (default: True) - flag (int): Window flag (default: 40 - touchable)

Returns: Current chain list length

"},{"location":"qsl4a/ui/floatview/#floatviewcount","title":"floatViewCount()","text":"

Get the number of active float views.

floatViewCount()\n

Returns: Number of float views

"},{"location":"qsl4a/ui/floatview/#floatviewresult","title":"floatViewResult()","text":"

Get the result/status of a float view.

floatViewResult(index=-1)\n

Parameters: - index (int): Float view index (default: -1, returns last operation result)

Returns: Dict with operation details including: - x, y: Position - time: Timestamp - operation: Operation type ('initial', 'move', etc.) - index: View index - removed: True if view was removed

"},{"location":"qsl4a/ui/floatview/#floatviewremove","title":"floatViewRemove()","text":"

Remove a float view.

floatViewRemove(index=-1)\n

Parameters: - index (int): Index of view to remove (default: -1 removes the last one)

Returns: 1 if successful, 0 otherwise

"},{"location":"qsl4a/ui/floatview/#constants","title":"Constants","text":"
  • floatView.INDEX_NEW = -1 - Create new float view
  • floatView.FLAG_DEFAULT_TOUCHABLE = 40 - Default touchable flag
  • floatView.TEXT_ALIGNMENT_INHERIT = 0
  • floatView.TEXT_ALIGNMENT_CENTER - Center text alignment
"},{"location":"qsl4a/ui/floatview/#usage-examples","title":"Usage Examples","text":""},{"location":"qsl4a/ui/floatview/#basic-float-view","title":"Basic Float View","text":"
import androidhelper\nfrom androidhelper import Android\n\ndroid = androidhelper.Android()\n\n# Create a simple float view\ndroid.floatView({\n    'index': -1,  # Create new\n    'text': 'Hello World',\n    'width': 400,\n    'height': 300,\n    'x': -300,  # Offset from center\n    'y': -400,\n    'backColor': 'ff0000',  # Red background\n    'textColor': '0000ff',  # Blue text\n    'textSize': 16,\n    'textAlign': droid.floatView.TEXT_ALIGNMENT_CENTER\n})\n\n# Check count\nprint(f\"Float views: {droid.floatViewCount().result}\")\n\n# Get result\nresult = droid.floatViewResult().result\nprint(f\"View info: {result}\")\n\n# Remove the float view\ndroid.floatViewRemove(0)\n
"},{"location":"qsl4a/ui/floatview/#html-content","title":"HTML Content","text":"
# Create float view with HTML content\ndroid.floatView({\n    'text': '',  # Empty text to use HTML\n    'html': '<h1>Title</h1><p>Rich <b>HTML</b> content</p>',\n    'width': 500,\n    'height': 400\n})\n
"},{"location":"qsl4a/ui/floatview/#modify-existing-view","title":"Modify Existing View","text":"
# Create initial view\ndroid.floatView({'text': 'Initial Text', 'width': 300, 'height': 150})\n\n# Modify the same view (index 0)\ndroid.floatView({\n    'index': 0,\n    'text': 'Updated Text!',\n    'backColor': '7f00ff00'  # Green background\n})\n\n# Check the move/change result\nresult = droid.floatViewResult(0).result\nprint(f\"Operation: {result.get('operation')}, Position: ({result.get('x')}, {result.get('y')})\")\n
"},{"location":"qsl4a/ui/floatview/#multiple-float-views","title":"Multiple Float Views","text":"
# Create multiple views\nfor i in range(3):\n    droid.floatView({\n        'index': -1,\n        'text': f'View {i}',\n        'x': i * 100 - 150,\n        'y': i * 100 - 150,\n        'backColor': f'{i}f{i}f{i}f'\n    })\n\nprint(f\"Total views: {droid.floatViewCount().result}\")\n\n# Remove all views\nwhile droid.floatViewCount().result > 0:\n    droid.floatViewRemove()\n
"},{"location":"qsl4a/ui/floatview/#with-script-callback","title":"With Script Callback","text":"
# Create float view that runs script on close\ndroid.floatView({\n    'text': 'Click to close and run script',\n    'script': '/sdcard/my_script.py',\n    'arg': 'hello from float view',\n    'clickRemove': True\n})\n
"},{"location":"qsl4a/ui/fullscreen/","title":"FullScreen UI","text":"

Create custom fullscreen interfaces with native Android layouts.

"},{"location":"qsl4a/ui/fullscreen/#layout-methods","title":"Layout Methods","text":""},{"location":"qsl4a/ui/fullscreen/#fullshow","title":"fullShow()","text":"

Show a fullscreen layout.

fullShow(layout, title=None, theme=None)\n

Parameters: - layout (str): JSON layout definition or layout string - title (str, optional): Window title - theme (str, optional): Theme name

Returns: Window ID

"},{"location":"qsl4a/ui/fullscreen/#fulldismiss","title":"fullDismiss()","text":"

Close fullscreen window.

fullDismiss()\n
"},{"location":"qsl4a/ui/fullscreen/#fullquery","title":"fullQuery()","text":"

Query all widget values.

fullQuery()\n

Returns: Dict of widget IDs and values

"},{"location":"qsl4a/ui/fullscreen/#fullquerydetail","title":"fullQueryDetail()","text":"

Query specific widget details.

fullQueryDetail(id)\n
"},{"location":"qsl4a/ui/fullscreen/#property-methods","title":"Property Methods","text":""},{"location":"qsl4a/ui/fullscreen/#fullgetproperty","title":"fullGetProperty()","text":"

Get widget property.

fullGetProperty(id, property)\n
"},{"location":"qsl4a/ui/fullscreen/#fullsetproperty","title":"fullSetProperty()","text":"

Set widget property.

fullSetProperty(id, property, value)\n
"},{"location":"qsl4a/ui/fullscreen/#fullsetlist","title":"fullSetList()","text":"

Set list widget items.

fullSetList(id, list, isHtml=False, listType=0)\n
"},{"location":"qsl4a/ui/fullscreen/#fullsetlist2","title":"fullSetList2()","text":"

Set list widget items with resource ID.

fullSetList2(id, list, intRes)\n

Parameters: - id (str): Widget ID - list (list): List of items - intRes (int): Android resource ID for list item layout

"},{"location":"qsl4a/ui/fullscreen/#fullsetlistselected","title":"fullSetListSelected()","text":"

Set selected item in a list.

fullSetListSelected(id, selected)\n

Parameters: - id (str): List widget ID - selected (int): Index of item to select

"},{"location":"qsl4a/ui/fullscreen/#fullgetlistselected","title":"fullGetListSelected()","text":"

Get the currently selected list item index.

fullGetListSelected(id)\n

Parameters: - id (str): List widget ID

Returns: Selected item index

"},{"location":"qsl4a/ui/fullscreen/#batch-property-operations","title":"Batch Property Operations","text":""},{"location":"qsl4a/ui/fullscreen/#fullgetproperties","title":"fullGetProperties()","text":"

Get properties for multiple widgets at once.

fullGetProperties(ids, property)\n

Parameters: - ids (list): List of widget IDs - property (str): Property name to get

Returns: Dict mapping widget IDs to property values

"},{"location":"qsl4a/ui/fullscreen/#fullsetproperties","title":"fullSetProperties()","text":"

Set property for multiple widgets at once.

fullSetProperties(ids, property, value)\n

Parameters: - ids (list): List of widget IDs - property (str): Property name to set - value: Property value

"},{"location":"qsl4a/ui/fullscreen/#screenshot","title":"Screenshot","text":""},{"location":"qsl4a/ui/fullscreen/#fullgetscreenshot","title":"fullGetScreenShot()","text":"

Capture fullscreen screenshot.

fullGetScreenShot(path=None)\n

Parameters: - path (str, optional): Save path. If None, returns in event

Returns: Event with screenshot data

"},{"location":"qsl4a/ui/fullscreen/#usage-example","title":"Usage Example","text":"
import androidhelper\n\ndroid = androidhelper.Android()\n\n# Define layout\nlayout = '''\n{ \"type\": \"LinearLayout\", \"orientation\": \"vertical\",\n  \"children\": [\n    { \"type\": \"TextView\", \"id\": \"title\", \"text\": \"Hello\" },\n    { \"type\": \"Button\", \"id\": \"btn\", \"text\": \"Click Me\" }\n  ]\n}\n'''\n\n# Show layout\ndroid.fullShow(layout, \"My App\")\n\n# Query button click\nevent = droid.eventWaitFor('click', timeout=10)\nif event.result:\n    widget_id = event.result['data']['id']\n    if widget_id == 'btn':\n        print(\"Button clicked!\")\n\ndroid.fullDismiss()\n
"}]} \ No newline at end of file diff --git a/en/sitemap.xml b/en/sitemap.xml new file mode 100644 index 0000000..5d1074f --- /dev/null +++ b/en/sitemap.xml @@ -0,0 +1,199 @@ + + + + https://www.qpython.org/en/ + 2026-04-21 + + + https://www.qpython.org/en/AIPyApp/ + 2026-04-21 + + + https://www.qpython.org/en/GraphicalInterface/ + 2026-04-21 + + + https://www.qpython.org/en/Notebook/ + 2026-04-21 + + + https://www.qpython.org/en/Ollama/ + 2026-04-21 + + + https://www.qpython.org/en/Terminal/ + 2026-04-21 + + + https://www.qpython.org/en/community/ + 2026-04-21 + + + https://www.qpython.org/en/editor-guide/ + 2026-04-21 + + + https://www.qpython.org/en/external-api/ + 2026-04-21 + + + https://www.qpython.org/en/getting-started/ + 2026-04-21 + + + https://www.qpython.org/en/qpypi-guide/ + 2026-04-21 + + + https://www.qpython.org/en/qpython-x/ + 2026-04-21 + + + https://www.qpython.org/en/tutorial-hello-world/ + 2026-04-21 + + + https://www.qpython.org/en/whats-new/ + 2026-04-21 + + + https://www.qpython.org/en/qsl4a/ + 2026-04-21 + + + https://www.qpython.org/en/qsl4a/connectivity/contacts/ + 2026-04-21 + + + https://www.qpython.org/en/qsl4a/connectivity/ftp/ + 2026-04-21 + + + https://www.qpython.org/en/qsl4a/connectivity/location/ + 2026-04-21 + + + https://www.qpython.org/en/qsl4a/connectivity/phone/ + 2026-04-21 + + + https://www.qpython.org/en/qsl4a/connectivity/signalstrength/ + 2026-04-21 + + + https://www.qpython.org/en/qsl4a/connectivity/sms/ + 2026-04-21 + + + https://www.qpython.org/en/qsl4a/connectivity/wifi/ + 2026-04-21 + + + https://www.qpython.org/en/qsl4a/core/android-base/ + 2026-04-21 + + + https://www.qpython.org/en/qsl4a/core/events/ + 2026-04-21 + + + https://www.qpython.org/en/qsl4a/core/intent/ + 2026-04-21 + + + https://www.qpython.org/en/qsl4a/hardware/bluetooth/ + 2026-04-21 + + + https://www.qpython.org/en/qsl4a/hardware/camera/ + 2026-04-21 + + + https://www.qpython.org/en/qsl4a/hardware/recorder/ + 2026-04-21 + + + https://www.qpython.org/en/qsl4a/hardware/usbserial/ + 2026-04-21 + + + https://www.qpython.org/en/qsl4a/hardware/webcam/ + 2026-04-21 + + + https://www.qpython.org/en/qsl4a/media/image/ + 2026-04-21 + + + https://www.qpython.org/en/qsl4a/media/mediaplayer/ + 2026-04-21 + + + https://www.qpython.org/en/qsl4a/special/cipher/ + 2026-04-21 + + + https://www.qpython.org/en/qsl4a/special/pgptai/ + 2026-04-21 + + + https://www.qpython.org/en/qsl4a/storage/clipboard/ + 2026-04-21 + + + https://www.qpython.org/en/qsl4a/storage/documentfile/ + 2026-04-21 + + + https://www.qpython.org/en/qsl4a/storage/preferences/ + 2026-04-21 + + + https://www.qpython.org/en/qsl4a/system/activityresult/ + 2026-04-21 + + + https://www.qpython.org/en/qsl4a/system/application/ + 2026-04-21 + + + https://www.qpython.org/en/qsl4a/system/battery/ + 2026-04-21 + + + https://www.qpython.org/en/qsl4a/system/qpyinterface/ + 2026-04-21 + + + https://www.qpython.org/en/qsl4a/system/sensors/ + 2026-04-21 + + + https://www.qpython.org/en/qsl4a/system/settings/ + 2026-04-21 + + + https://www.qpython.org/en/qsl4a/system/sysinfo/ + 2026-04-21 + + + https://www.qpython.org/en/qsl4a/system/wakelock/ + 2026-04-21 + + + https://www.qpython.org/en/qsl4a/ui/accessibility/ + 2026-04-21 + + + https://www.qpython.org/en/qsl4a/ui/dialogs/ + 2026-04-21 + + + https://www.qpython.org/en/qsl4a/ui/floatview/ + 2026-04-21 + + + https://www.qpython.org/en/qsl4a/ui/fullscreen/ + 2026-04-21 + + \ No newline at end of file diff --git a/en/sitemap.xml.gz b/en/sitemap.xml.gz new file mode 100644 index 0000000..32fd50d Binary files /dev/null and b/en/sitemap.xml.gz differ diff --git a/source/en/static/1.png b/en/static/1.png similarity index 100% rename from source/en/static/1.png rename to en/static/1.png diff --git a/source/en/static/2.png b/en/static/2.png similarity index 100% rename from source/en/static/2.png rename to en/static/2.png diff --git a/source/en/static/3.png b/en/static/3.png similarity index 100% rename from source/en/static/3.png rename to en/static/3.png diff --git a/source/en/static/aipyapp_demo.jpg b/en/static/aipyapp_demo.jpg similarity index 100% rename from source/en/static/aipyapp_demo.jpg rename to en/static/aipyapp_demo.jpg diff --git a/source/en/static/bestpython.png b/en/static/bestpython.png old mode 100755 new mode 100644 similarity index 100% rename from source/en/static/bestpython.png rename to en/static/bestpython.png diff --git a/source/en/static/extra.css b/en/static/extra.css similarity index 100% rename from source/en/static/extra.css rename to en/static/extra.css diff --git a/source/en/static/googledrive-.png b/en/static/googledrive-.png similarity index 100% rename from source/en/static/googledrive-.png rename to en/static/googledrive-.png diff --git a/source/en/static/googledrive.jpg b/en/static/googledrive.jpg similarity index 100% rename from source/en/static/googledrive.jpg rename to en/static/googledrive.jpg diff --git a/source/en/static/guide_extend_pic1.png b/en/static/guide_extend_pic1.png similarity index 100% rename from source/en/static/guide_extend_pic1.png rename to en/static/guide_extend_pic1.png diff --git a/source/en/static/guide_extend_pic2.png b/en/static/guide_extend_pic2.png similarity index 100% rename from source/en/static/guide_extend_pic2.png rename to en/static/guide_extend_pic2.png diff --git a/source/en/static/guide_helloworld_pic1.png b/en/static/guide_helloworld_pic1.png similarity index 100% rename from source/en/static/guide_helloworld_pic1.png rename to en/static/guide_helloworld_pic1.png diff --git a/source/en/static/guide_howtostart_pic1.png b/en/static/guide_howtostart_pic1.png similarity index 100% rename from source/en/static/guide_howtostart_pic1.png rename to en/static/guide_howtostart_pic1.png diff --git a/source/en/static/guide_howtostart_pic2.png b/en/static/guide_howtostart_pic2.png similarity index 100% rename from source/en/static/guide_howtostart_pic2.png rename to en/static/guide_howtostart_pic2.png diff --git a/source/en/static/guide_howtostart_pic3.png b/en/static/guide_howtostart_pic3.png similarity index 100% rename from source/en/static/guide_howtostart_pic3.png rename to en/static/guide_howtostart_pic3.png diff --git a/source/en/static/guide_howtostart_pic4.png b/en/static/guide_howtostart_pic4.png similarity index 100% rename from source/en/static/guide_howtostart_pic4.png rename to en/static/guide_howtostart_pic4.png diff --git a/source/en/static/guide_howtostart_pic5.png b/en/static/guide_howtostart_pic5.png similarity index 100% rename from source/en/static/guide_howtostart_pic5.png rename to en/static/guide_howtostart_pic5.png diff --git a/source/en/static/guide_ide_qedit4web.png b/en/static/guide_ide_qedit4web.png old mode 100755 new mode 100644 similarity index 100% rename from source/en/static/guide_ide_qedit4web.png rename to en/static/guide_ide_qedit4web.png diff --git a/source/en/static/guide_ide_qedit4web_choose.png b/en/static/guide_ide_qedit4web_choose.png similarity index 100% rename from source/en/static/guide_ide_qedit4web_choose.png rename to en/static/guide_ide_qedit4web_choose.png diff --git a/source/en/static/guide_ide_qedit4web_develop.png b/en/static/guide_ide_qedit4web_develop.png similarity index 100% rename from source/en/static/guide_ide_qedit4web_develop.png rename to en/static/guide_ide_qedit4web_develop.png diff --git a/source/en/static/guide_program_pic1.png b/en/static/guide_program_pic1.png similarity index 100% rename from source/en/static/guide_program_pic1.png rename to en/static/guide_program_pic1.png diff --git a/source/en/static/img_banner2x.jpg b/en/static/img_banner2x.jpg similarity index 100% rename from source/en/static/img_banner2x.jpg rename to en/static/img_banner2x.jpg diff --git a/source/en/static/img_logo.png b/en/static/img_logo.png similarity index 100% rename from source/en/static/img_logo.png rename to en/static/img_logo.png diff --git a/source/en/static/mov_hellolorld.mp4 b/en/static/mov_hellolorld.mp4 similarity index 100% rename from source/en/static/mov_hellolorld.mp4 rename to en/static/mov_hellolorld.mp4 diff --git a/source/en/static/qpy_dashboard.jpg b/en/static/qpy_dashboard.jpg similarity index 100% rename from source/en/static/qpy_dashboard.jpg rename to en/static/qpy_dashboard.jpg diff --git a/source/en/static/qpy_editor.jpg b/en/static/qpy_editor.jpg similarity index 100% rename from source/en/static/qpy_editor.jpg rename to en/static/qpy_editor.jpg diff --git a/source/en/static/qpy_helloworld.jpg b/en/static/qpy_helloworld.jpg similarity index 100% rename from source/en/static/qpy_helloworld.jpg rename to en/static/qpy_helloworld.jpg diff --git a/source/en/static/qpython_graphical_interace_demo.jpg b/en/static/qpython_graphical_interace_demo.jpg similarity index 100% rename from source/en/static/qpython_graphical_interace_demo.jpg rename to en/static/qpython_graphical_interace_demo.jpg diff --git a/source/en/static/sl4a.jpg b/en/static/sl4a.jpg similarity index 100% rename from source/en/static/sl4a.jpg rename to en/static/sl4a.jpg diff --git a/source/en/static/taskerplugin-for-qpython.png b/en/static/taskerplugin-for-qpython.png similarity index 100% rename from source/en/static/taskerplugin-for-qpython.png rename to en/static/taskerplugin-for-qpython.png diff --git a/source/en/static/terminal_demo.jpg b/en/static/terminal_demo.jpg similarity index 100% rename from source/en/static/terminal_demo.jpg rename to en/static/terminal_demo.jpg diff --git a/source/en/static/wecomdrive-.png b/en/static/wecomdrive-.png similarity index 100% rename from source/en/static/wecomdrive-.png rename to en/static/wecomdrive-.png diff --git a/source/en/static/wecomdrive.jpg b/en/static/wecomdrive.jpg similarity index 100% rename from source/en/static/wecomdrive.jpg rename to en/static/wecomdrive.jpg diff --git a/en/tutorial-hello-world/index.html b/en/tutorial-hello-world/index.html new file mode 100644 index 0000000..cc41fc2 --- /dev/null +++ b/en/tutorial-hello-world/index.html @@ -0,0 +1,2654 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Hello World Tutorial - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + +

Writing "Hello World"

+

Hello world

+

hello world

+

Well, after you became a bit more familiar with QPython, let's create our first program in QPython. Obviously, it will be helloworld.py. ;)

+

Start QPython, open editor and enter the following code:

+
#qpy:quiet
+import androidhelper
+
+droid = androidhelper.Android()
+droid.makeToast('Hello, QPYTHON!')
+
+

No wonder, it's just similar to any other hello-world program. When executed, it just shows pop-up message on the screen (see screenshot on the top). Anyway, it's a good example of QPython program.

+

Code Understanding

+

It begins with import androidhelper — the most useful module in QPython, which encapsulates almost all interface with Android available in Python. Any script developed in QPython starts with this statement (at least if it claims to communicate with user). Read more about Python library here and import statement here.

+

Next, we create an object droid (actually a class), which is necessary to call RPC functions to communicate with Android.

+

The last line of our code calls droid.makeToast(), which shows a small pop-up message (a "toast") on the screen.

+

Well, let's add some more functionality. Let it ask the user name and greet them.

+

More samples

+

We can display a simple dialog box with the title, prompt, edit field and buttons Ok and Cancel using dialogGetInput call. Replace the last line of your code and save it as hello1.py:

+
#qpy:quiet
+import androidhelper
+
+droid = androidhelper.Android()
+respond = droid.dialogGetInput("Hello", "What is your name?")
+
+

Well, I think it should return any respond, any user reaction. That's why I wrote respond = .... But what does the call actually return? Let's check. Just add print statement after the last line:

+
#qpy:quiet
+import androidhelper
+
+droid = androidhelper.Android()
+respond = droid.dialogGetInput("Hello", "What is your name?")
+print(respond)
+
+

Then save and run it...

+

Oops! Nothing printed? Don't worry. Just pull notification bar and you will see "QPython Program Output: hello1.py" — tap it!

+

As you can see, droid.dialogGetInput() returns a JSON object with three fields. We need only one — result which contains an actual input from user.

+

Let's add script's reaction:

+
#qpy:quiet
+import androidhelper
+
+droid = androidhelper.Android()
+respond = droid.dialogGetInput("Hello", "What is your name?")
+print(respond)
+message = f'Hello, {respond.result}!'
+droid.makeToast(message)
+
+

Last two lines (1) format the message and (2) show the message to the user in the toast. See Python docs if you still don't know what f-strings mean.

+

Wow! It works! ;)

+

Now I'm going to add a bit of logic there. Think: what happen if the user clicks Cancel button, or clicks Ok leaving the input field blank?

+

You can play with the program checking what contains respond variable in every case.

+

First of all, I want to put text entered by user to a separate variable: name = respond.result. Then I'm going to check it, and if it contains any real text, it will be considered as a name and will be used in greeting. Otherwise another message will be shown. Replace fifth line message = f'Hello, {respond.result}!' with the following code:

+
name = respond.result
+if name:
+    message = f'Hello, {name}!'
+else:
+    message = "Hey! And you're not very polite, %Username%!"
+
+

Use < and > buttons on the toolbar to indent/unindent lines in if-statement (or just use space/backspace keys). You can read more about indentation in Python here; if-statement described here.

+

First of all, we put user input to the variable name. Then we check does name contain anything? In case the user left the line blank and clicked Ok, the return value is empty string ''. In case of Cancel button pressed, the return value is None. Both are treated as false in if-statement. So, only if name contains anything meaningful, then-statement is executed and greeting "Hello, ...!" shown. In case of empty input the user will see "Hey! And you're not very polite, %Username%!" message.

+

Ok, here is the whole program:

+
#qpy:quiet
+import androidhelper
+
+droid = androidhelper.Android()
+respond = droid.dialogGetInput("Hello", "What is your name?")
+print(respond)
+name = respond.result
+if name:
+    message = f'Hello, {name}!'
+else:
+    message = "Hey! And you're not very polite, %Username%!"
+droid.makeToast(message)
+
+

Execution Result

+ + +

Next Steps

+

For Python beginners, we recommend learning from the Python Basic Syntax course to further your Python skills, or browse QPython Featured Courses to find more content you want to learn.

+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/en/whats-new/index.html b/en/whats-new/index.html new file mode 100644 index 0000000..aa63ae7 --- /dev/null +++ b/en/whats-new/index.html @@ -0,0 +1,2979 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Updates - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

What's New

+

v4.0.0

+
    +
  • External storage access: Users can now save Python scripts directly to external storage, greatly improving file management flexibility.
  • +
  • QSL4A enhancements: Improved QSL4A features. (https://www.qpython.org/en/qsl4a/)
  • +
  • Community & Courses: Refined community and course modules with clearer info and better navigation for easier access to learning resources and support.
  • +
+

v3.9.2 / v3.9.3

+
    +
  • SDK upgrade with support for 16 KB page size, providing a smoother runtime environment
  • +
  • Major editor updates for a more fluid editing experience
  • +
  • Various other minor improvements
  • +
+

v3.9.1

+
    +
  • SDK upgrade with support for 16 KB page size, providing a smoother runtime environment
  • +
  • Extension packages now support MCP
  • +
  • Bug fixes
  • +
+

v3.9.0

+
    +
  • SDK upgrade to incorporate the latest Android features
  • +
  • Built-in Ollama 0.9.5 upgrade: Now supports running Gemma3n models locally in QPython, experiencing more powerful on-device AI computing capabilities
  • +
  • Pygame game development support: Combined with XServer, you can now easily write and run Pygame games on QPython, unleashing your creativity!
  • +
  • Personalized icon customization: Customize your desktop icons and themes through QPython settings, creating a unique programming environment
  • +
+

v3.8.11

+
    +
  • Built-in Ollama 0.9.5 upgrade: Now supports running Gemma3n models locally in QPython, experiencing more powerful on-device AI computing capabilities
  • +
  • Pygame game development support: Combined with XServer, you can now easily write and run Pygame games on QPython, unleashing your creativity!
  • +
  • Personalized icon customization: Customize your desktop icons and themes through QPython settings, creating a unique programming environment
  • +
+

v3.8.10

+
    +
  • Built-in Ollama upgrade: Now you can easily run Qwen3 / Gemma3 models locally in QPython!
  • +
  • Powerful MCP library added: Added mcp library in Extensions -> AIPY, allowing you to take powerful MCP functionality with you anywhere, anytime!
  • +
+

v3.8.9

+

Major update! AI programming fully integrated into QPython to make your programming easier!

+
    +
  • Natural language programming support: New support for AIPyApp, enabling natural language programming in QPython. Currently in beta - join our community for usage instructions
  • +
  • New QSL4A feature: Added notebookOpen function, supporting natural language control to open Notebook files
  • +
  • Built-in editor upgrade: Enhanced editor functionality, supporting opening and browsing various text files
  • +
  • Convenient file management: Added internal storage entry in file manager for quick access to your files
  • +
+

v3.8.8

+
    +
  • Important improvements to file access permission controls, allowing users to flexibly enable or disable access to external storage files through QPython programming
  • +
  • SDK upgraded to enhance support and compatibility with newer Android versions
  • +
  • Added Anthropic and Google GenAI libraries in Extensions -> AIPY
  • +
+

v3.8.7

+
    +
  • Based on existing Tkinter support, added sample code for drawing Doraemon using Turtle in Extensions -> Tools
  • +
  • Optimized phone permission acquisition process, improving user experience and operational convenience
  • +
  • Added Google Gen AI library in Extensions -> AIPY for easy access to Gemini Developer API and Vertex AI
  • +
+

v3.8.6

+
    +
  • Tkinter support: Now supports Tkinter library, accessible through XServer. Please visit www.qpython.org's drive link "Extra" to download required files
  • +
  • Storage permission update: To improve user experience, we added permission to read phone storage for accessing Python programs stored in other directories
  • +
+

After this update, QPython and QPythonPlus (not available on app stores, designed for advanced users with more sensitive permissions) will maintain synchronized version numbers.

+

v3.5.2 (2025/2/25)

+

Achieve seamless integration with the open source LLM deployment tool Ollama and DeepSeek developed by DeepSeek!

+

Features: +- Zero threshold to run various large language models locally on mobile devices +- Quickly deploy cutting-edge AI models such as DeepSeek +- Enjoy a minimalist API calling experience +- Build a completely offline intelligent programming environment

+

Capabilities: +- Load/manage LLM models directly on the mobile phone +- Real-time low-latency response based on local computing

+

v3.5.0

+
    +
  • Python upgraded to 3.12.8
  • +
  • Improved Dashboard for clearer, more user-friendly functionality
  • +
  • Added numerous third-party packages: PyTorch / Twisted / Scrapy / FastAPI ...
  • +
+

v3.3.8

+
    +
  • Fixed NumPy compatibility issues
  • +
+

v3.3.5

+
    +
  • Upgraded Python kernel to 3.11.9
  • +
  • Fixed bug where module installation status was not displayed in extensions
  • +
  • Fixed bug where deleting modules in extensions failed
  • +
  • Fixed other bugs
  • +
+

v3.3.4

+
    +
  • Added OpenAI/Langchain/APIGPTCloud AI packages
  • +
  • Removed unnecessary files to reduce size
  • +
+

v3.2.5

+
    +
  • Added some SL4A functions
  • +
  • Other bug fixes
  • +
+

v3.2.3

+
    +
  • Updated Python version to 3.11.0
  • +
  • Updated IPython version to 8.6.0
  • +
  • Updated pip version to 22.3.1
  • +
  • Updated scientific computing packages with automatic soft links
  • +
  • Reduced space usage
  • +
  • Added some SL4A functions + other bug fixes
  • +
+

v3.1.8

+
    +
  • Added operation hotkeys in terminal
  • +
  • Fixed issue where editor lost content on rotation
  • +
  • Fixed other bugs
  • +
+
+

Download on Google Play

+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/source/huawei.html b/huawei.html similarity index 100% rename from source/huawei.html rename to huawei.html diff --git a/build.sh b/index.html old mode 100755 new mode 100644 similarity index 54% rename from build.sh rename to index.html index 80c0b04..fcf6426 --- a/build.sh +++ b/index.html @@ -1,33 +1,3 @@ -#!/bin/bash - -# Build script for QPython documentation -# Supports both English and Chinese - -set -e - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -cd "$SCRIPT_DIR" - -PYTHON="python3.12" - -echo "Building QPython documentation..." -echo "" - -# Clean previous build -echo "Cleaning previous build..." -rm -rf site - -# Build English site -echo "Building English site..." -$PYTHON -m mkdocs build - -# Build Chinese site -echo "Building Chinese site..." -$PYTHON -m mkdocs build -f mkdocs-zh.yml - -# Create root index.html -echo "Creating root index.html..." -cat > site/index.html << 'EOF' @@ -91,31 +61,3 @@

Choose your language / 选择语言

-EOF - -# Copy additional HTML files from source to site -if ls source/*.html 1> /dev/null 2>&1; then - echo "Copying additional HTML files..." - cp source/*.html site/ -fi - -# Copy CNAME file if exists -if [ -f source/CNAME ]; then - echo "Copying CNAME file..." - cp source/CNAME site/ -fi - -# Remove "Made with Material for MkDocs" footer from all HTML files -echo "Removing MkDocs Material footer..." -find site -name "*.html" -exec sed -i '/Made with/,/<\/a>/d' {} \; - -echo "" -echo "Build complete!" -echo "" -echo "Output directories:" -echo " - English: site/en/" -echo " - Chinese: site/zh/" -echo " - Root: site/index.html" -echo "" -echo "To preview locally:" -echo " cd site && python -m http.server 8000" diff --git a/mkdocs-zh.yml b/mkdocs-zh.yml deleted file mode 100644 index 3b02a8d..0000000 --- a/mkdocs-zh.yml +++ /dev/null @@ -1,125 +0,0 @@ -site_name: QPython -site_url: https://www.qpython.org/zh/ -site_description: QPython - Android 上的 Python & AI 开发环境 -theme: - name: material - logo: static/img_logo.png - favicon: static/img_logo.png - palette: - - scheme: default - primary: black - accent: green - toggle: - icon: material/brightness-7 - name: 切换暗色模式 - - scheme: slate - primary: black - accent: green - toggle: - icon: material/brightness-4 - name: 切换亮色模式 - features: - - navigation.tabs - - navigation.sections - - navigation.expand - - search.suggest - - search.highlight - - content.code.copy - font: - text: Roboto - code: Roboto Mono - -copyright: © QPython 2012-2026 - -extra_css: - - static/extra.css - -docs_dir: source/zh -site_dir: site/zh - -nav: - - 首页: - - 概览: index.md - - 版本: qpython-x.md - - 更新日志: whats-new.md - - 教程: - - 快速入门: getting-started.md - - Hello World 教程: tutorial-hello-world.md - - 指南: - - 终端: Terminal.md - - Notebook: Notebook.md - - 编辑器: editor-guide.md - - QPYPI: qpypi-guide.md - - 图形界面: GraphicalInterface.md - - AIPyApp: AIPyApp.md - - Ollama: Ollama.md - - 开放API: external-api.md - - QSL4A: - - 概览: qsl4a/index.md - - 核心: - - Android 基础: qsl4a/core/android-base.md - - Intent 系统: qsl4a/core/intent.md - - 事件系统: qsl4a/core/events.md - - UI: - - 对话框: qsl4a/ui/dialogs.md - - 全屏 UI: qsl4a/ui/fullscreen.md - - 悬浮窗: qsl4a/ui/floatview.md - - 辅助功能: qsl4a/ui/accessibility.md - - 系统: - - 电池: qsl4a/system/battery.md - - 传感器: qsl4a/system/sensors.md - - 应用: qsl4a/system/application.md - - 系统信息: qsl4a/system/sysinfo.md - - 设置: qsl4a/system/settings.md - - 唤醒锁: qsl4a/system/wakelock.md - - QPython 接口: qsl4a/system/qpyinterface.md - - Activity 结果: qsl4a/system/activityresult.md - - 硬件: - - 蓝牙: qsl4a/hardware/bluetooth.md - - 相机: qsl4a/hardware/camera.md - - 音频/录音: qsl4a/hardware/recorder.md - - 网络摄像头: qsl4a/hardware/webcam.md - - USB 串口: qsl4a/hardware/usbserial.md - - 连接: - - WiFi: qsl4a/connectivity/wifi.md - - 位置: qsl4a/connectivity/location.md - - 短信: qsl4a/connectivity/sms.md - - 电话: qsl4a/connectivity/phone.md - - 联系人: qsl4a/connectivity/contacts.md - - 信号强度: qsl4a/connectivity/signalstrength.md - - FTP 服务器: qsl4a/connectivity/ftp.md - - 存储: - - DocumentFile: qsl4a/storage/documentfile.md - - 剪贴板: qsl4a/storage/clipboard.md - - 偏好设置: qsl4a/storage/preferences.md - - 媒体: - - 媒体播放器: qsl4a/media/mediaplayer.md - - 图像处理: qsl4a/media/image.md - - 特殊功能: - - 加密: qsl4a/special/cipher.md - - PGPT AI: qsl4a/special/pgptai.md - -markdown_extensions: - - pymdownx.highlight: - anchor_linenums: true - - pymdownx.inlinehilite - - pymdownx.mark - - pymdownx.snippets - - pymdownx.superfences - - tables - - toc: - permalink: true - -extra: - analytics: - provider: google - property: G-3GZ8CTQYNC - social: - - icon: fontawesome/brands/github - link: https://github.com/qpython-android/qpython - - icon: fontawesome/brands/discord - link: https://discord.gg/hV2chuD - - icon: fontawesome/brands/facebook - link: http://www.facebook.com/qpython - - icon: fontawesome/brands/twitter - link: http://www.twitter.com/qpython diff --git a/mkdocs.yml b/mkdocs.yml deleted file mode 100644 index 7ed195f..0000000 --- a/mkdocs.yml +++ /dev/null @@ -1,115 +0,0 @@ -site_name: QPython -site_url: https://www.qpython.org/en/ -site_description: QPython - AI-Enabled Python IDE for Android -theme: - name: material - logo: static/img_logo.png - favicon: static/img_logo.png - palette: - - scheme: default - primary: black - accent: green - toggle: - icon: material/brightness-7 - name: Switch to dark mode - - scheme: slate - primary: black - accent: green - toggle: - icon: material/brightness-4 - name: Switch to light mode - features: - - navigation.tabs - - navigation.sections - - navigation.expand - - search.suggest - - search.highlight - - content.code.copy - font: - text: Roboto - code: Roboto Mono - -copyright: © QPython 2012-2026 - -extra_css: - - static/extra.css - -docs_dir: source/en -site_dir: site/en - -nav: - - Home: - - Overview: index.md - - Branches: qpython-x.md - - Updates: whats-new.md - - Tutorial: - - Getting Started: getting-started.md - - Hello World Tutorial: tutorial-hello-world.md - - Guides: - - Terminal: Terminal.md - - Notebook: Notebook.md - - Editor: editor-guide.md - - QPYPI: qpypi-guide.md - - Graphical Interface: GraphicalInterface.md - - AIPyApp: AIPyApp.md - - Ollama: Ollama.md - - OpenAPI: external-api.md - - QSL4A: - - Overview: qsl4a/index.md - - Core: - - Android Base: qsl4a/core/android-base.md - - Intent System: qsl4a/core/intent.md - - Event System: qsl4a/core/events.md - - UI: - - Dialogs: qsl4a/ui/dialogs.md - - FullScreen UI: qsl4a/ui/fullscreen.md - - FloatView: qsl4a/ui/floatview.md - - Accessibility: qsl4a/ui/accessibility.md - - System: - - Battery: qsl4a/system/battery.md - - Sensors: qsl4a/system/sensors.md - - Application: qsl4a/system/application.md - - System Info: qsl4a/system/sysinfo.md - - Hardware: - - Bluetooth: qsl4a/hardware/bluetooth.md - - Camera: qsl4a/hardware/camera.md - - Audio/Recorder: qsl4a/hardware/recorder.md - - Connectivity: - - WiFi: qsl4a/connectivity/wifi.md - - Location: qsl4a/connectivity/location.md - - SMS: qsl4a/connectivity/sms.md - - Storage: - - DocumentFile: qsl4a/storage/documentfile.md - - Clipboard: qsl4a/storage/clipboard.md - - Media: - - Media Player: qsl4a/media/mediaplayer.md - - Image Processing: qsl4a/media/image.md - - Special: - - Cipher: qsl4a/special/cipher.md - - PGPT AI: qsl4a/special/pgptai.md - - -markdown_extensions: - - pymdownx.highlight: - anchor_linenums: true - - pymdownx.inlinehilite - - pymdownx.mark - - pymdownx.snippets - - pymdownx.superfences - - tables - - toc: - permalink: true - -extra: - analytics: - provider: google - property: G-3GZ8CTQYNC - social: - - icon: fontawesome/brands/github - link: https://github.com/qpython-android/qpython - - icon: fontawesome/brands/discord - link: https://discord.gg/hV2chuD - - icon: fontawesome/brands/facebook - link: http://www.facebook.com/qpython - - icon: fontawesome/brands/twitter - link: http://www.twitter.com/qpython diff --git a/source/privacy-cn.html b/privacy-cn.html similarity index 100% rename from source/privacy-cn.html rename to privacy-cn.html diff --git a/source/privacy.html b/privacy.html similarity index 100% rename from source/privacy.html rename to privacy.html diff --git a/source/qlua-privacy.html b/qlua-privacy.html similarity index 100% rename from source/qlua-privacy.html rename to qlua-privacy.html diff --git a/source/qlua-rate.html b/qlua-rate.html similarity index 100% rename from source/qlua-rate.html rename to qlua-rate.html diff --git a/source/qq.html b/qq.html similarity index 100% rename from source/qq.html rename to qq.html diff --git a/qsl4a-android/README.md b/qsl4a-android/README.md deleted file mode 100644 index b0b3383..0000000 --- a/qsl4a-android/README.md +++ /dev/null @@ -1,690 +0,0 @@ -# QSL4A (QPython Scripting Layer for Android) - Codebase Analysis - -## 1. Package Structure - -The QSL4A codebase is located at `/qsl4a/src/main/java/org/qpython/qsl4a/` and is organized into the following packages: - -| Package | Purpose | -|---------|---------| -| `org.qpython.qsl4a` (root) | Main entry points (QSL4APP, QPyScriptService, QSL4AScript) | -| `org.qpython.qsl4a.codec` | Base64 encoding/decoding for data serialization | -| `org.qpython.qsl4a.facade` | Android API facades (30+ classes) | -| `org.qpython.qsl4a.facade.ui` | UI-related facade components | -| `org.qpython.qsl4a.facade.usb` | USB serial communication facades | -| `org.qpython.qsl4a.qsl4a` | Core infrastructure | -| `org.qpython.qsl4a.qsl4a.event` | Event system | -| `org.qpython.qsl4a.qsl4a.exception` | Exception types | -| `org.qpython.qsl4a.qsl4a.future` | Async activity handling | -| `org.qpython.qsl4a.qsl4a.interpreter` | Interpreter management | -| `org.qpython.qsl4a.qsl4a.jsonrpc` | JSON-RPC mechanism | -| `org.qpython.qsl4a.qsl4a.language` | Language support | -| `org.qpython.qsl4a.qsl4a.rpc` | RPC annotations and descriptors | -| `org.qpython.qsl4a.qsl4a.trigger` | Event trigger system | -| `org.qpython.qsl4a.qsl4a.util` | Utility classes | - ---- - -## 2. Core Classes - -### 2.1 Main Entry Points - -#### QSL4APP (`org.qpython.qsl4a.QSL4APP`) -**Purpose:** Application class that initializes the QSL4A runtime environment. - -**Key Methods:** -- `initQSL4APP()` - Initializes interpreter configuration and starts discovering interpreters -- `getTaskExecutor()` -> `FutureActivityTaskExecutor` - Returns the task executor -- `getInterpreterConfiguration()` -> `InterpreterConfiguration` - Returns interpreter config -- `getTriggerRepository()` -> `TriggerRepository` - Returns the trigger repository -- `readyToStart()` -> `boolean` - Blocks until configuration is updated - -#### QPyScriptService (`org.qpython.qsl4a.QPyScriptService`) -**Purpose:** Android Service that hosts the JSON-RPC server for script execution. - -**Key Methods:** -- `onCreate()` - Starts the service and initializes the proxy -- `onDestroy()` - Shuts down the proxy and RPC server -- `onBind(Intent intent)` -> `IBinder` - Returns binder for service connection -- `onStartCommand(Intent intent, int flags, int startId)` -> `int` - Returns START_STICKY -- `start(Context context)` - Static method to start the service -- `stop(Context context)` - Static method to stop the service -- `startToast(Context context)` - Shows toast when service starts - -#### QSL4AScript (`org.qpython.qsl4a.QSL4AScript`) -**Purpose:** Utility class for script file handling. - -**Key Methods:** -- `getFileName(Context context)` -> `String` - Returns the main script filename (default: "main.py") -- `getFileExtension(Context context)` -> `String` - Returns file extension (e.g., ".py") - ---- - -### 2.2 AndroidProxy (`org.qpython.qsl4a.qsl4a.AndroidProxy`) -**Purpose:** Central proxy class that manages the RPC server and facade factories. - -**Key Methods:** -- `AndroidProxy(Service service, Intent intent)` - Constructor; creates JSON-RPC server with secret UUID -- `getAddress()` -> `InetSocketAddress` - Returns server address -- `startLocal()` / `startLocal(int port)` - Starts RPC server on localhost -- `shutdown()` - Shuts down the RPC server -- `getSecret()` -> `String` - Returns the authentication secret -- `getRpcReceiverManagerFactory()` -> `RpcReceiverManagerFactory` - Returns the facade factory - ---- - -### 2.3 SimpleServer (`org.qpython.qsl4a.qsl4a.SimpleServer`) -**Purpose:** Abstract base class for TCP socket servers handling concurrent connections. - -**Key Methods:** -- `startLocal(int port)` -> `InetSocketAddress` - Starts server on localhost -- `startPublic(int port)` -> `InetSocketAddress` - Starts server on public interface -- `startAllInterfaces(int port)` -> `InetSocketAddress` - Starts server on all interfaces -- `shutdown()` - Stops the server and closes all connections -- `getNumberOfConnections()` -> `int` - Returns active connection count -- `addObserver(SimpleServerObserver)` / `removeObserver(SimpleServerObserver)` - Observer pattern - -**Abstract Methods:** -- `handleConnection(Socket socket)` - Subclasses implement to handle client connections - ---- - -## 3. Facade Classes (Android API Access) - -All facades extend `RpcReceiver` and are managed by `FacadeManager`. They expose Android functionality via RPC-annotated methods. - -### 3.1 FacadeManager (`org.qpython.qsl4a.facade.FacadeManager`) -**Purpose:** Manages all facade instances and handles RPC invocation with SDK version checking. - -**Key Methods:** -- `FacadeManager(int sdkLevel, Service service, Intent intent, Collection> classList)` - Constructor -- `getSdkLevel()` -> `int` - Returns Android SDK level -- `getService()` -> `Service` - Returns the Android service -- `getIntent()` -> `Intent` - Returns the launch intent -- `invoke(Class clazz, Method method, Object[] args)` -> `Object` - Invokes RPC with deprecation/minSdk checks -- `getReceiver(Class clazz)` -> `T` - Gets or creates a facade instance - -### 3.2 FacadeConfiguration (`org.qpython.qsl4a.facade.FacadeConfiguration`) -**Purpose:** Registry of all available facade classes. - -**Registered Facades (30 total):** -- `AndroidFacade` - Core Android operations -- `ApplicationManagerFacade` - App management -- `CameraFacade` - Camera access -- `CommonIntentsFacade` - Common Android intents -- `ContactsFacade` - Contacts access -- `EventFacade` - Event queue management -- `LocationFacade` - GPS/Location -- `PhoneFacade` - Phone operations -- `MediaRecorderFacade` - Audio recording -- `SensorManagerFacade` - Device sensors -- `SettingsFacade` - System settings -- `SmsFacade` - SMS operations -- `SpeechRecognitionFacade` - Voice input -- `ToneGeneratorFacade` - DTMF tones -- `WakeLockFacade` - Power management -- `WifiFacade` - WiFi operations -- `UiFacade` - UI operations -- `BatteryManagerFacade` - Battery info -- `MediaPlayerFacade` - Media playback -- `PreferencesFacade` - Shared preferences -- `QPyInterfaceFacade` - QPython-specific -- `USBHostSerialFacade` - USB serial -- `CipherFacade` - Cryptography -- `TextToSpeechFacade` - TTS -- `BluetoothFacade` - Bluetooth -- `SignalStrengthFacade` - Signal info -- `WebCamFacade` - Camera -- `FloatViewFacade` - Overlay views -- `DocumentFileFacade` - Document access -- `HarmonyOsFacade` - HarmonyOS compatibility -- `FtpFacade` - FTP server -- `AccessibilityFacade` - Accessibility services - -### 3.3 AndroidFacade (`org.qpython.qsl4a.facade.AndroidFacade`) -**Purpose:** Main facade providing core Android functionality. - -**Key RPC Methods:** -- `setClipboard(String text)` / `getClipboard()` -> `String` - Clipboard operations -- `startActivity(String action, String uri, ...)` - Start activities -- `startActivityForResult(String action, ...)` -> `Intent` - Activity with result -- `sendBroadcast(String action, ...)` - Send broadcasts -- `makeIntent(String action, ...)` -> `Intent` - Create intents -- `makeToast(String message, int length, ...)` - Show toast notifications -- `notify(String title, String message, String uri, ...)` - Show persistent notifications -- `getNetworkStatus()` -> `boolean` - Check network -- `getIntent()` -> `Object` - Get launch intent -- `getPackageVersion(String packageName)` -> `String` - Get package version -- `environment()` -> `Map` - Environment info -- `getConstants(String classname)` -> `Bundle` - Get class constants -- `vibrate(int duration)` - Vibrate device - -### 3.4 EventFacade (`org.qpython.qsl4a.facade.EventFacade`) -**Purpose:** Event queue management for async event handling. - -**Key RPC Methods:** -- `eventClearBuffer()` - Clears all events from buffer -- `eventPoll(int number_of_events)` -> `List` - Returns and removes oldest n events -- `eventWaitFor(String eventName, Integer timeout)` -> `Event` - Blocks until event occurs -- `eventWait(Integer timeout)` -> `Event` - Blocks until any event occurs -- `eventPost(String name, String data, Boolean enqueue)` - Post event to queue -- `startEventDispatcher(int port)` -> `int` - Opens socket for event streaming -- `stopEventDispatcher()` - Stops event server -- `eventRegisterForBroadcast(String category, Boolean enqueue)` -> `boolean` - Register broadcast listener -- `eventUnregisterForBroadcast(String category)` - Unregister broadcast listener -- `eventGetBrodcastCategories()` -> `Set` - Get registered categories - -### 3.5 SensorManagerFacade (`org.qpython.qsl4a.facade.SensorManagerFacade`) -**Purpose:** Access device sensors (accelerometer, magnetometer, light, gyroscope, etc.). - -**Key RPC Methods:** -- `startSensingTimed(int sensorNumber, int delayTime)` - Starts sensor data collection -- `startSensingThreshold(int sensorNumber, int threshold, int axis)` - Threshold-based sensing -- `readSensors()` -> `Bundle` - Returns most recent sensor data -- `stopSensing()` - Stops sensor collection -- `sensorsGetAccuracy()` -> `Integer` - Get sensor accuracy -- `sensorsGetLight()` -> `Float` - Get light level -- `sensorsReadAccelerometer()` -> `List` - [x, y, z] acceleration -- `sensorsReadMagnetometer()` -> `List` - [x, y, z] magnetic field -- `sensorsReadGyroscope()` -> `List` - [x, y, z] angular speed -- `sensorsReadOrientation()` -> `List` - [azimuth, pitch, roll] -- `sensorsGetStepCounter()` -> `Integer` - Step count - -### 3.6 LocationFacade (`org.qpython.qsl4a.facade.LocationFacade`) -**Purpose:** GPS and network location services. - -**Key RPC Methods:** -- `startLocating(int minUpdateTime, int minUpdateDistance, boolean updateGnssStatus)` - Start location updates -- `readLocation()` -> `Map` - Get current location by provider -- `readGnssStatus()` -> `JSONArray` - Get GNSS satellite status (Android 8+) -- `stopLocating()` - Stop location updates -- `getLastKnownLocation()` -> `Map` - Get last known location -- `geocode(double latitude, double longitude, int maxResults)` -> `JSONObject[]` - Reverse geocode -- `locationProviders()` -> `List` - Get available providers -- `locationProviderEnabled(String provider)` -> `boolean` - Check if provider enabled - ---- - -## 4. RPC Mechanism - -### 4.1 JSON-RPC Server Architecture - -``` -JsonRpcServer (extends SimpleServer) - | - +-- handleConnection(Socket socket) - | | - | +-- Reads JSON requests line-by-line - | +-- First RPC must be _authenticate with secret - | +-- Looks up MethodDescriptor by method name - | +-- Invokes method and returns JsonRpcResult - | - +-- RpcReceiverManagerFactory - | - +-- FacadeManagerFactory - | - +-- Creates FacadeManager instances - +-- Each FacadeManager manages multiple RpcReceivers (Facades) -``` - -### 4.2 JsonRpcServer (`org.qpython.qsl4a.qsl4a.jsonrpc.JsonRpcServer`) -**Purpose:** TCP-based JSON-RPC server that handles client connections. - -**Key Methods:** -- `JsonRpcServer(RpcReceiverManagerFactory managerFactory, String handshake)` - Constructor -- `handleConnection(Socket socket)` - Processes JSON-RPC requests -- `shutdown()` - Notifies all receivers and stops server - -**Request Format:** -```json -{"id": 1, "method": "methodName", "params": [arg1, arg2]} -``` - -**Response Format:** -```json -{"id": 1, "result": {...}, "error": null} -``` - -### 4.3 RpcReceiverManager (`org.qpython.qsl4a.qsl4a.jsonrpc.RpcReceiverManager`) -**Purpose:** Base class managing RPC receivers (facades) and method lookup. - -**Key Methods:** -- `RpcReceiverManager(Collection> classList)` - Initializes receivers and collects RPC methods -- `getReceiver(Class clazz)` -> `T` - Gets or lazily creates facade instance -- `getMethodDescriptor(String methodName)` -> `MethodDescriptor` - Looks up RPC method -- `invoke(Class clazz, Method method, Object[] args)` -> `Object` - Invokes method via reflection -- `shutdown()` - Calls shutdown on all receivers - -### 4.4 RpcReceiver (`org.qpython.qsl4a.qsl4a.jsonrpc.RpcReceiver`) -**Purpose:** Abstract base class for all facades. - -**Methods:** -- `RpcReceiver(RpcReceiverManager manager)` - Constructor; stores manager reference -- `shutdown()` - Abstract; facades implement cleanup - -### 4.5 JsonRpcResult (`org.qpython.qsl4a.qsl4a.jsonrpc.JsonRpcResult`) -**Purpose:** Utility class for building JSON-RPC response objects. - -**Static Methods:** -- `empty(int id)` -> `JSONObject` - Creates empty result response -- `result(int id, Object data)` -> `JSONObject` - Creates success response -- `error(int id, Throwable t)` -> `JSONObject` - Creates error response - -### 4.6 JsonBuilder (`org.qpython.qsl4a.qsl4a.jsonrpc.JsonBuilder`) -**Purpose:** Converts Java objects to JSON for RPC responses. - -**Key Methods:** -- `build(Object data)` -> `Object` - Converts Java object to JSON-compatible type -- `buildBundleItem(Object data)` -> `Object` - Handles Bundle, Uri, byte[] special cases -- `buildJsonMap(Map map)` -> `JSONObject` - Converts Map to JSONObject -- `buildJsonList(List list)` -> `JSONArray` - Converts List to JSONArray -- `buildJsonIntent(Intent intent)` -> `JSONObject` - Converts Intent to JSON -- `buildJsonEvent(Event event)` -> `JSONObject` - Converts Event to JSON - -**Supported Type Conversions:** -- `null` -> `JSONObject.NULL` -- `Integer, Long, Double` -> primitive values -- `String, Boolean` -> as-is -- `JSONObject, JSONArray` -> as-is -- `List` -> `JSONArray` -- `Set` -> `JSONArray` -- `Map` -> `JSONObject` -- `Intent` -> `{"data", "type", "extras", "categories", "action", "packagename", "classname", "flags"}` -- `Bundle` -> `JSONObject` with special markers (`\0uri\0`, `\0byte\0`) -- `Event` -> `{"name", "data", "time"}` - -### 4.7 MethodDescriptor (`org.qpython.qsl4a.qsl4a.rpc.MethodDescriptor`) -**Purpose:** Describes an RPC method, handles parameter conversion, and invokes methods. - -**Key Methods:** -- `collectFrom(Class clazz)` -> `Collection` - Static; collects @Rpc-annotated methods -- `invoke(RpcReceiverManager manager, JSONArray parameters)` -> `Object` - Invokes RPC with parameter conversion -- `convertParameter(JSONArray parameters, int index, Type type)` -> `Object` - Converts JSON param to Java type -- `buildIntent(JSONObject jsonObject)` -> `Intent` - Builds Intent from JSON -- `getName()` -> `String` - Returns RPC name (or @RpcName value) -- `getHelp()` -> `String` - Generates help text from annotations - -**Parameter Type Conversions:** -- `Boolean.class` - from JSON boolean or integer (0/1) -- `Long.class` - from JSON long -- `Double.class` - from JSON double -- `Integer.class` - from JSON int -- `Intent.class` - from JSONObject using `buildIntent()` -- Other types - direct cast from JSON - ---- - -## 5. RPC Annotations - -### 5.1 @Rpc (`org.qpython.qsl4a.qsl4a.rpc.Rpc`) -**Purpose:** Marks a method as an RPC endpoint. - -**Attributes:** -- `description()` -> `String` - Brief description of the function -- `returns()` -> `String` - Description of return value (default: "") - -### 5.2 @RpcParameter (`org.qpython.qsl4a.qsl4a.rpc.RpcParameter`) -**Purpose:** Documents RPC parameters. - -**Attributes:** -- `name()` -> `String` - Formal parameter name -- `description()` -> `String` - Parameter description (default: "") - -### 5.3 @RpcDefault (`org.qpython.qsl4a.qsl4a.rpc.RpcDefault`) -**Purpose:** Specifies default value for optional parameters. - -**Attributes:** -- `value()` -> `String` - Default value as string -- `converter()` -> `Class` - Type converter (default: Converter.class) - -### 5.4 @RpcOptional (`org.qpython.qsl4a.qsl4a.rpc.RpcOptional`) -**Purpose:** Marks parameter as optional with null default. - -### 5.5 @RpcName (`org.qpython.qsl4a.qsl4a.rpc.RpcName`) -**Purpose:** Overrides the RPC method name. - -**Attributes:** -- `name()` -> `String` - Custom RPC name - -### 5.6 @RpcDeprecated (`org.qpython.qsl4a.qsl4a.rpc.RpcDeprecated`) -**Purpose:** Marks RPC as deprecated. - -**Attributes:** -- `value()` -> `String` - Replacement method name -- `release()` -> `String` - Release version when deprecated - -### 5.7 @RpcMinSdk (`org.qpython.qsl4a.qsl4a.rpc.RpcMinSdk`) -**Purpose:** Specifies minimum Android SDK version required. - -**Attributes:** -- `value()` -> `int` - Minimum SDK level - -### 5.8 @RpcStartEvent (`org.qpython.qsl4a.qsl4a.rpc.RpcStartEvent`) -**Purpose:** Indicates RPC starts an event stream. - -**Attributes:** -- `value()` -> `String` - Event name - -### 5.9 @RpcStopEvent (`org.qpython.qsl4a.qsl4a.rpc.RpcStopEvent`) -**Purpose:** Indicates RPC stops an event stream. - -**Attributes:** -- `value()` -> `String` - Event name - ---- - -## 6. Interpreter System - -### 6.1 Interpreter (`org.qpython.qsl4a.qsl4a.interpreter.Interpreter`) -**Purpose:** Represents a language interpreter with execution parameters. - -**Key Methods:** -- `buildFromMaps(Map data, Map env, Map args)` -> `Interpreter` - Static factory -- `getName()` -> `String` - Interpreter name (e.g., "python") -- `getNiceName()` -> `String` - Display name -- `getExtension()` -> `String` - File extension (e.g., ".py") -- `getBinary()` -> `File` - Path to interpreter binary -- `getScriptCommand()` -> `String` - Command template for script execution -- `getInteractiveCommand()` -> `String` - Command for interactive mode -- `hasInteractiveMode()` -> `boolean` - Whether interactive mode supported -- `getArguments()` -> `List` - CLI arguments -- `getEnvironmentVariables()` -> `Map` - Environment variables -- `getLanguage()` -> `Language` - Associated language object -- `getRpcText(String content, MethodDescriptor rpc, String[] values)` -> `String` - Generates RPC call code -- `isInstalled()` -> `boolean` - Whether binary exists - -### 6.2 InterpreterConfiguration (`org.qpython.qsl4a.qsl4a.interpreter.InterpreterConfiguration`) -**Purpose:** Discovers and manages installed interpreters via Android package intents. - -**Key Methods:** -- `startDiscovering()` / `startDiscovering(String mime)` - Starts interpreter discovery -- `isDiscoveryComplete()` -> `boolean` - Whether discovery finished -- `registerObserver(ConfigurationObserver observer)` - Register for config changes -- `getSupportedInterpreters()` -> `List` - All known interpreters -- `getInstalledInterpreters()` -> `List` - Only installed ones -- `getInteractiveInterpreters()` -> `List` - Those with interactive mode -- `getInterpreterByName(String name)` -> `Interpreter` - Find by name -- `getInterpreterForScript(String scriptName)` -> `Interpreter` - Find by file extension - -**Discovery Mechanism:** -- Queries package manager for activities handling `InterpreterConstants.MIME + extension` -- Reads interpreter properties from ContentProvider -- Broadcasts `ACTION_INTERPRETER_ADDED` / `ACTION_INTERPRETER_REMOVED` - -### 6.3 InterpreterDescriptor (`org.qpython.qsl4a.qsl4a.interpreter.InterpreterDescriptor`) -**Purpose:** Metadata about an interpreter version. - -**Attributes:** -- `getVersion()` -> `String` - Version string -- `getName()` -> `String` - Interpreter name -- `isInstalled()` -> `boolean` - Installation status - -### 6.4 SupportedLanguages (`org.qpython.qsl4a.qsl4a.language.SupportedLanguages`) -**Purpose:** Registry mapping file extensions to Language classes. - -**Supported Languages:** -| Extension | Language Class | -|-----------|---------------| -| `.html` | HtmlLanguage | -| `.bsh` | BeanShellLanguage | -| `.js` | JavaScriptLanguage | -| `.lua` | LuaLanguage | -| `.pl` | PerlLanguage | -| `.py` | PythonLanguage | -| `.rb` | RubyLanguage | -| `.tcl` | TclLanguage | -| `.php` | PhpLanguage | -| `.sl` | SleepLanguage | -| `.nut` | SquirrelLanguage | - -### 6.5 Language (`org.qpython.qsl4a.qsl4a.language.Language`) -**Purpose:** Base class for language-specific code generation. - -**Key Methods:** -- `getContentTemplate()` -> `String` - Template for new scripts -- `getImportStatement()` -> `String` - Import statement (overridden per language) -- `getRpcReceiverDeclaration(String rpcReceiver)` -> `String` - RPC receiver instantiation -- `getDefaultRpcReceiver()` -> `String` - Default receiver name (default: "droid") -- `getRpcText(String content, MethodDescriptor rpc, String[] values)` -> `String` - Generates method call -- `autoClose(char token)` -> `String` - Returns closing token for auto-complete - -### 6.6 PythonLanguage (`org.qpython.qsl4a.qsl4a.language.PythonLanguage`) -**Purpose:** Python-specific code generation. - -**Generated Template:** -```python -import android - -droid = android.Android() -``` - ---- - -## 7. Trigger System - -### 7.1 TriggerRepository (`org.qpython.qsl4a.qsl4a.trigger.TriggerRepository`) -**Purpose:** Persistent storage and management of event triggers. - -**Key Methods:** -- `put(Trigger trigger)` - Adds trigger and ensures TriggerService running -- `remove(Trigger trigger)` - Removes trigger -- `getAllTriggers()` -> `Multimap` - All triggers by event name -- `isEmpty()` -> `boolean` - Whether any triggers exist -- `addObserver(TriggerRepositoryObserver observer)` - Register for changes -- `bootstrapObserver(TriggerRepositoryObserver observer)` - Register and notify of existing triggers -- `removeObserver(TriggerRepositoryObserver observer)` - Unregister - -**Persistence:** -- Serializes to Base64-encoded object stream -- Stores in SharedPreferences under key "TRIGGERS" - -### 7.2 Trigger (`org.qpython.qsl4a.qsl4a.trigger.Trigger`) -**Purpose:** Interface for event-driven script execution. - -**Methods:** -- `handleEvent(Event event, Context context)` - Called when trigger event occurs -- `getEventName()` -> `String` - Event name this trigger listens for - -### 7.3 ScriptTrigger (`org.qpython.qsl4a.qsl4a.trigger.ScriptTrigger`) -**Purpose:** Trigger implementation that launches a script file. - -**Key Methods:** -- `ScriptTrigger(String eventName, File script)` - Constructor -- `handleEvent(Event event, Context context)` - Starts script via IntentBuilders -- `getEventName()` -> `String` - Returns registered event name -- `getScript()` -> `File` - Returns script file - -### 7.4 Event (`org.qpython.qsl4a.qsl4a.event.Event`) -**Purpose:** Immutable event object for the event queue. - -**Attributes:** -- `mName` - Event name -- `mData` - Event data (any object) -- `mCreationTime` - Creation timestamp (milliseconds * 1000) - -**Key Methods:** -- `Event(String name, Object data)` - Constructor -- `getName()` / `setName(String name)` - Name accessor -- `getData()` / `setData(Object data)` - Data accessor -- `getCreationTime()` -> `double` - Timestamp -- `nameEquals(String name)` -> `boolean` - Name comparison - -### 7.5 EventFacade.EventObserver (`org.qpython.qsl4a.facade.EventFacade.EventObserver`) -**Purpose:** Interface for event notification. - -**Methods:** -- `onEventReceived(Event event)` - Called when event occurs - -### 7.6 EventServer (`org.qpython.qsl4a.facade.EventServer`) -**Purpose:** TCP server that streams events to connected clients. - -**Key Methods:** -- `EventServer(int port)` - Constructor; starts server on specified port -- `getAddress()` -> `InetSocketAddress` - Server address -- `onEventReceived(Event event)` - Broadcasts event to all listeners -- `shutdown()` - Notifies listeners and stops server - ---- - -## 8. Async/Future System - -### 8.1 FutureResult (`org.qpython.qsl4a.qsl4a.future.FutureResult`) -**Purpose:** Blocking future for async operation results. - -**Key Methods:** -- `set(T result)` - Sets result and unblocks waiters -- `get()` -> `T` - Blocks until result available -- `get(long timeout, TimeUnit unit)` -> `T` - Blocks with timeout -- `isDone()` -> `boolean` - Whether result available -- `cancel(boolean mayInterruptIfRunning)` -> `boolean` - Always returns false - -### 8.2 FutureActivityTask (`org.qpython.qsl4a.qsl4a.future.FutureActivityTask`) -**Purpose:** Activity lifecycle wrapper for async UI operations. - -**Key Methods:** -- `onCreate()` / `onStart()` / `onResume()` / `onPause()` / `onStop()` / `onDestroy()` - Lifecycle callbacks -- `onActivityResult(int requestCode, int resultCode, Intent data)` - Handle activity result -- `setActivity(Activity activity)` / `getActivity()` -> `Activity` - Activity access -- `setTaskDescription(String title)` - Set task description (for recent apps) -- `setResult(T result)` - Set the task result -- `getResult()` -> `T` - Get the result (blocks) -- `finish()` - Finish the activity -- `startActivity(Intent intent)` / `startActivityForResult(Intent intent, int requestCode)` - Start activities - -### 8.3 FutureActivityTaskExecutor (`org.qpython.qsl4a.qsl4a.future.FutureActivityTaskExecutor`) -**Purpose:** Executes FutureActivityTask instances on the main thread. - -**Key Methods:** -- `execute(FutureActivityTask task)` - Execute task with activity lifecycle - ---- - -## 9. File Structure Summary - -``` -/qsl4a/src/main/java/org/qpython/qsl4a/ -├── QSL4APP.java # Application class -├── QPyScriptService.java # Android Service (RPC server host) -├── QSL4AScript.java # Script file utilities -├── codec/ # Base64 encoding -├── facade/ # Android API facades (30+ classes) -│ ├── AndroidFacade.java # Core Android operations -│ ├── EventFacade.java # Event queue management -│ ├── LocationFacade.java # GPS/Location -│ ├── SensorManagerFacade.java # Device sensors -│ ├── UiFacade.java # UI operations -│ └── ... (25+ more facades) -├── qsl4a/ -│ ├── AndroidProxy.java # Central proxy/RPC server -│ ├── SimpleServer.java # TCP socket server base -│ ├── Constants.java # Intent/action constants -│ ├── event/ -│ │ └── Event.java # Event data class -│ ├── exception/ -│ │ └── Sl4aException.java # Base exception -│ ├── future/ -│ │ ├── FutureResult.java # Async result holder -│ │ ├── FutureActivityTask.java # Activity lifecycle wrapper -│ │ └── FutureActivityTaskExecutor.java -│ ├── interpreter/ -│ │ ├── Interpreter.java # Interpreter descriptor -│ │ ├── InterpreterConfiguration.java # Interpreter discovery -│ │ └── Language.java # Base language class -│ ├── jsonrpc/ -│ │ ├── JsonRpcServer.java # JSON-RPC TCP server -│ │ ├── RpcReceiver.java # Base facade class -│ │ ├── RpcReceiverManager.java # Manages facades -│ │ ├── RpcReceiverManagerFactory.java -│ │ ├── JsonRpcResult.java # Response builder -│ │ └── JsonBuilder.java # JSON serialization -│ ├── language/ -│ │ ├── Language.java # Base language -│ │ ├── PythonLanguage.java # Python-specific -│ │ └── SupportedLanguages.java # Extension registry -│ ├── rpc/ -│ │ ├── Rpc.java # @Rpc annotation -│ │ ├── RpcParameter.java # @RpcParameter annotation -│ │ ├── RpcDefault.java # @RpcDefault annotation -│ │ ├── RpcOptional.java # @RpcOptional annotation -│ │ ├── RpcName.java # @RpcName annotation -│ │ ├── RpcDeprecated.java # @RpcDeprecated annotation -│ │ ├── RpcMinSdk.java # @RpcMinSdk annotation -│ │ ├── RpcStartEvent.java # @RpcStartEvent annotation -│ │ ├── RpcStopEvent.java # @RpcStopEvent annotation -│ │ └── MethodDescriptor.java # RPC method descriptor -│ ├── trigger/ -│ │ ├── Trigger.java # Trigger interface -│ │ ├── ScriptTrigger.java # Script launch trigger -│ │ └── TriggerRepository.java # Persistent trigger storage -│ └── util/ # Utilities -``` - ---- - -## 10. Key Data Flow - -### 10.1 Script Execution Flow -1. Script is launched (via Intent or direct start) -2. `QPyScriptService` starts via `AndroidProxy` -3. `JsonRpcServer` starts on localhost with random port -4. Script connects via TCP socket -5. First RPC must be `_authenticate(secret)` -6. Script calls RPCs like `droid.makeToast("Hello")` -7. `JsonRpcServer` looks up `MethodDescriptor` via `RpcReceiverManager` -8. `MethodDescriptor.invoke()` converts JSON params to Java types -9. Facade method is invoked via reflection -10. Result is converted back to JSON via `JsonBuilder` -11. Response sent back to client - -### 10.2 Event Flow -1. Facade calls `eventPost("sensors", data)` or similar -2. `EventFacade.postEvent()` creates `Event` object -3. Event added to `ConcurrentLinkedQueue` (max 1024) -4. Named event observers are notified -5. Global event observers are notified -6. `EventServer` broadcasts to all connected TCP clients - -### 10.3 Trigger Flow -1. `TriggerRepository.put(trigger)` stores trigger -2. Trigger serialized to SharedPreferences -3. When matching event occurs, `Trigger.handleEvent()` is called -4. `ScriptTrigger` starts script via `IntentBuilders.buildStartInBackgroundIntent()` - ---- - -## 11. All Facade Classes Reference - -| Facade Class | Purpose | -|--------------|---------| -| `AccessibilityFacade` | Accessibility services | -| `ActivityResultFacade` | Activity result handling | -| `AndroidFacade` | Core Android operations | -| `ApplicationManagerFacade` | Application management | -| `BatteryManagerFacade` | Battery status | -| `BluetoothFacade` | Bluetooth operations | -| `CameraFacade` | Camera access | -| `CipherFacade` | Cryptography/Encryption | -| `CommonIntentsFacade` | Common Android intents | -| `ContactsFacade` | Contacts access | -| `DocumentFileFacade` | Document file access | -| `EventFacade` | Event queue management | -| `EventServer` | TCP event streaming | -| `FloatViewFacade` | Overlay/floating views | -| `FtpFacade` | FTP server | -| `HarmonyOsFacade` | HarmonyOS compatibility | -| `LocationFacade` | GPS/Location | -| `MediaPlayerFacade` | Media playback | -| `MediaRecorderFacade` | Audio/video recording | -| `MjpegServer` | MJPEG streaming | -| `PhoneFacade` | Phone operations | -| `PreferencesFacade` | Shared preferences | -| `QPyInterfaceFacade` | QPython-specific interface | -| `SensorManagerFacade` | Device sensors | -| `SettingsFacade` | System settings | -| `SignalStrengthFacade` | Signal strength | -| `SmsFacade` | SMS operations | -| `SpeechRecognitionFacade` | Speech recognition | -| `TextToSpeechFacade` | Text-to-speech | -| `ToneGeneratorFacade` | DTMF tones | -| `WakeLockFacade` | Power management | -| `WebCamFacade` | Webcam streaming | -| `WifiFacade` | WiFi operations | diff --git a/qsl4a-android/SL4A.md b/qsl4a-android/SL4A.md deleted file mode 100644 index 5c7aac9..0000000 --- a/qsl4a-android/SL4A.md +++ /dev/null @@ -1,667 +0,0 @@ -# QPython SL4A Facade RPC Methods - -This document lists all @Rpc annotated methods from the QPython SL4A facade classes. - ---- - -## AndroidFacade - -| RPC Method | Parameters | Return | Description | -|------------|------------|--------|-------------| -| setClipboard | text(String) | void | Put text in the clipboard. | -| getClipboard | | String | Read text from the clipboard. | -| startActivityForResult | action(String), uri(optional), type(optional), extras(optional), packagename(optional), classname(optional), flags(optional) | Intent | Starts an activity and returns the result. | -| startActivityForResultIntent | intent(Intent) | Intent | Starts an activity and returns the result. | -| startActivity | action(String), uri(optional), type(optional), extras(optional), wait(optional), packagename(optional), classname(optional), flags(optional) | void | Starts an activity. | -| sendBroadcast | action(String), uri(optional), type(optional), extras(optional), packagename(optional), classname(optional), flags(optional) | void | Send a broadcast. | -| makeIntent | action(String), uri(optional), type(optional), extras(optional), categories(optional), packagename(optional), classname(optional), flags(optional) | Intent | Create an Intent. | -| startActivityIntent | intent(Intent), wait(optional) | void | Start Activity using Intent | -| sendBroadcastIntent | intent(Intent) | void | Send Broadcast Intent | -| vibrate | duration(Integer, default=300) | void | Vibrates the phone or a specified duration in milliseconds. | -| makeToast | message(String), length(Integer, default=0), isHtml(Boolean, default=false), backColor(optional), textColor(optional), textSize(Integer, default=0), textAlign(Integer, default=0) | void | Displays a Toast notification. | -| notify | title(String), message(String), uri(optional), arg(optional) | void | Displays a notification that will be canceled when the user clicks on it. | -| getNetworkStatus | | boolean | Returns the status of network connection. | -| getIntent | | Object | Returns the intent that launched the script. | -| getPackageVersionCode | packageName(String) | int | Returns package version code. | -| getPackageVersion | packageName(String) | String | Returns package version name. | -| log | message(String) | void | Writes message to logcat. | -| environment | | Map | A map of various useful environment details | -| getConstants | classname(String) | Bundle | Get list of constants (static final fields) for a class | - ---- - -## EventFacade - -| RPC Method | Parameters | Return | Description | -|------------|------------|--------|-------------| -| eventClearBuffer | | void | Clears all events from the event buffer. | -| eventRegisterForBroadcast | category(String), enqueue(Boolean, default=true) | boolean | Registers a listener for a new broadcast signal | -| eventUnregisterForBroadcast | category(String) | void | Stop listening for a broadcast signal | -| eventGetBrodcastCategories | | Set | Lists all the broadcast signals we are listening for | -| eventPoll | number_of_events(Integer, default=1) | List | Returns and removes the oldest n events from the event buffer. | -| eventWaitFor | eventName(String), timeout(optional) | Event | Blocks until an event with the supplied name occurs. | -| eventWait | timeout(optional) | Event | Blocks until an event occurs. | -| eventPost | name(String), data(String), enqueue(Boolean, default=false, optional) | void | Post an event to the event queue. | -| rpcPostEvent | name(String), data(String) | void | Post an event to the event queue. (**Deprecated**, use eventPost) | -| receiveEvent | | Event | Returns and removes the oldest event from the event buffer. (**Deprecated**, use eventPoll) | -| waitForEvent | eventName(String), timeout(optional) | Event | Blocks until an event with the supplied name occurs. (**Deprecated**, use eventWaitFor) | -| startEventDispatcher | port(Integer, default=0, optional) | int | Opens up a socket where you can read for events posted | -| stopEventDispatcher | | void | Stops the event server, you can't read in the port anymore | - ---- - -## LocationFacade - -| RPC Method | Parameters | Return | Description | -|------------|------------|--------|-------------| -| locationProviders | | List | Returns availables providers on the phone | -| locationProviderEnabled | provider(String) | boolean | Ask if provider is enabled | -| startLocating | minUpdateTime(Integer, default=60000), minUpdateDistance(Integer, default=30), updateGnssStatus(Boolean, default=false) | void | Starts collecting location data. (starts event "location") | -| readLocation | | Map | Returns the current location as indicated by all available providers. | -| readGnssStatus | | JSONArray | read Global Navigation Satellite System status if Android >= 8. | -| stopLocating | | void | Stops collecting location data. (stops event "location") | -| getLastKnownLocation | | Map | Returns the last known location of the device. | -| geocode | latitude(Double), longitude(Double), maxResults(Integer, default=1) | JSONObject[] | Returns a list of addresses for the given latitude and longitude. | - ---- - -## SensorManagerFacade - -| RPC Method | Parameters | Return | Description | -|------------|------------|--------|-------------| -| startSensingTimed | sensorNumber(Integer), delayTime(Integer) | void | Starts recording sensor data to be available for polling. (starts event "sensors") | -| startSensingThreshold | sensorNumber(Integer), threshold(Integer), axis(Integer) | void | Records to the Event Queue sensor data exceeding a chosen threshold. (starts event "threshold") | -| readSensors | | Bundle | Returns the most recently recorded sensor data. | -| stopSensing | | void | Stops collecting sensor data. (stops event "sensors") | -| sensorsGetAccuracy | | Integer | Returns the most recently received accuracy value. | -| sensorsGetLight | | Float | Returns the most recently received light value. | -| sensorsGetStepCounter | | Integer | Returns the most recently step counter. | -| sensorsReadAccelerometer | | List | Returns the most recently received accelerometer values. | -| sensorsReadMagnetometer | | List | Returns the most recently received magnetic field values. | -| sensorsReadGyroscope | | List | Returns the most recently received Gyroscope values. | -| sensorsReadOrientation | | List | Returns the most recently received orientation values. | - ---- - -## PhoneFacade - -| RPC Method | Parameters | Return | Description | -|------------|------------|--------|-------------| -| startTrackingPhoneState | | void | Starts tracking phone state. (starts event "phone") | -| readPhoneState | | Bundle | Returns the current phone state and incoming number. | -| stopTrackingPhoneState | | void | Stops tracking phone state. (stops event "phone") | -| phoneCall | uri(String) | void | Calls a contact/phone number by URI. | -| phoneCallNumber | phone number(String) | void | Calls a phone number. | -| phoneDial | uri(String) | void | Dials a contact/phone number by URI. | -| phoneDialNumber | phone number(String) | void | Dials a phone number. | -| getCellLocation | | JSONObject | Returns the current cell location. | -| getAllCellsLocation | | JSONArray | Returns all the cells location. | -| getNetworkOperator | | String | Returns the numeric name (MCC+MNC) of current registered operator. | -| getNetworkOperatorName | | String | Returns the alphabetic name of current registered operator. | -| getNetworkType | | String | Returns a the radio technology (network type) currently in use on the device. | -| getPhoneType | | String | Returns the device phone type. | -| getSimCountryIso | | String | Returns the ISO country code equivalent for the SIM provider's country code. | -| getSimOperator | | String | Returns the MCC+MNC (mobile country code + mobile network code) of the provider of the SIM. | -| getSimOperatorName | | String | Returns the Service Provider Name (SPN). | -| getSimSerialNumber | | String | Returns the serial number of the SIM, if applicable. | -| getSimState | | String | Returns the state of the device SIM card. | -| getSubscriberId | | String | Returns the unique subscriber ID, for example, the IMSI for a GSM phone. | -| getVoiceMailAlphaTag | | String | Retrieves the alphabetic identifier associated with the voice mail number. | -| getVoiceMailNumber | | String | Returns the voice mail number. | -| checkNetworkRoaming | | Boolean | Returns true if the device is considered roaming on the current network. | -| getDeviceId | index(Integer, default=0) | String | Returns the unique device ID (IMEI for GSM, MEID for CDMA). (**Deprecated**) | -| getMeid | index(optional) | String | MEID for CDMA phones, Need Android >= 8.0. | -| getImei | index(optional) | String | IMEI for GSM phones, Need Android >= 8.0. | -| getDeviceSoftwareVersion | | String | Returns the software version number for the device. | -| getLine1Number | | String | Returns the phone number string for line 1. (requires API O) | -| getAllCellInfo | | List | Returns all the neighboring cell information of the device. | -| setDataEnabled | enable(Boolean) | void | set data enabled | - ---- - -## SmsFacade - -| RPC Method | Parameters | Return | Description | -|------------|------------|--------|-------------| -| smsSend | destinationAddress(String), text(String) | void | Sends an SMS. | -| smsGetMessageCount | unreadOnly(Boolean), folder(String, default="inbox") | Integer | Returns the number of messages. | -| smsGetMessageIds | unreadOnly(Boolean), folder(String, default="inbox") | List | Returns a List of all message IDs. | -| smsGetMessages | unreadOnly(Boolean), folder(String, default="inbox"), attributes(optional) | List | Returns a List of all messages. | -| smsGetMessageById | id(Integer), attributes(optional) | JSONObject | Returns message attributes. | -| smsGetAttributes | | List | Returns a List of all possible message attributes. | -| smsDeleteMessage | id(Integer) | Boolean | Deletes a message. | -| smsMarkMessageRead | ids(JSONArray), read(Boolean) | Integer | Marks messages as read. | - ---- - -## CameraFacade - -| RPC Method | Parameters | Return | Description | -|------------|------------|--------|-------------| -| cameraCapturePicture | targetPath(optional), cameraId(Integer, default=0), useAutoFocus(Boolean, default=true) | String | Take a picture and save it to the specified path. | -| takePicture | path(optional) | String | Take Picture with system camera. | -| takeVideo | path(optional), quality(Integer, default=1) | String | Take Video with system camera. | -| cameraSetTorchMode | enabled(Boolean) | void | open or close flash light torch of camera. | - ---- - -## MediaRecorderFacade - -| RPC Method | Parameters | Return | Description | -|------------|------------|--------|-------------| -| recorderStartMicrophone | path(optional) | String | Records audio from the microphone and saves it to the given location. | -| recorderStop | | void | Stops a previously started recording. | -| recorderPause | | void | Pause a previously started recording. | -| recorderResume | | void | Resume a previously paused recording. | -| recordAudio | | String | Record Audio with system soundrecorder. | -| recorderStartScreenRecord | path(optional), audio(Integer, default=1), targetPixels(optional), frameRate(Integer, default=30), bitRate(optional), rotation(Boolean, default=false), autoStart(Boolean, default=true) | String | Record screen to a file. | -| recorderStart | | void | Start Media Recorder. | -| imageReaderGetScreenShot | path(optional), delayMilliSec(Integer, default=1000) | String | Capture ScreenShot. | -| recorderCaptureVideo | targetPath(optional), duration(Integer, default=10), cameraId(Integer, default=0), quality(Integer, default=8) | String | Records video from the camera and saves it to the given location. | -| recorderSoundVolumeGetDb | | double | Recorder Sound Volumn Get Db. | -| recorderSoundVolumeDetect | interval(Integer, default=100) | boolean | Recorder Sound Volumn Detect. | -| imageCompress | srcPath(String), destPath(String), targetByteSize(Integer, default=0), targetWidth(Integer, default=0), targetHeight(Integer, default=0) | long | image Compress by targetByteSize, targetWidth, targetHeight. | - ---- - -## MediaPlayerFacade - -| RPC Method | Parameters | Return | Description | -|------------|------------|--------|-------------| -| mediaPlay | url(String), tag(String, default="default"), play(Boolean, default=true) | boolean | Open a media file | -| mediaPlayPause | tag(String, default="default") | boolean | pause playing media file | -| mediaPlayStart | tag(String, default="default") | boolean | start playing media file | -| mediaPlayClose | tag(String, default="default") | boolean | Close media file | -| mediaIsPlaying | tag(String, default="default") | boolean | Checks if media file is playing. | -| mediaPlayInfo | tag(String, default="default") | Map | Information on current media | -| mediaPlayList | | Set | Lists currently loaded media | -| mediaPlaySetLooping | enabled(Boolean, default=true), tag(String, default="default") | boolean | Set Looping | -| mediaPlaySeek | msec(Integer), tag(String, default="default") | int | Seek To Position | -| musicPlay | url(String) | boolean | Play a music file (**Deprecated**) | - ---- - -## WifiFacade - -| RPC Method | Parameters | Return | Description | -|------------|------------|--------|-------------| -| wifiGetScanResults | | JSONArray | Returns the list of access points found during the most recent Wifi scan. | -| wifiLockAcquireFull | | void | Acquires a full Wifi lock. | -| wifiLockAcquireScanOnly | | void | Acquires a scan only Wifi lock. | -| wifiLockRelease | | void | Releases a previously acquired Wifi lock. | -| wifiStartScan | | Boolean | Starts a scan for Wifi access points. | -| checkWifiState | | Boolean | Checks Wifi state. | -| toggleWifiState | enabled(optional) | Boolean | Toggle Wifi on and off. | -| wifiDisconnect | | Boolean | Disconnects from the currently active access point. | -| wifiGetConnectionInfo | ipConvertToString(Boolean, default=true) | JSONObject | Returns information about the currently active access point. | -| wifiReassociate | | Boolean | Reassociates with the currently active access point. | -| wifiReconnect | | Boolean | Reconnects to the currently active access point. | -| wifiGetApState | | String | get wifi ap state. | -| getConnectedInfo | | JSONArray | get connected hot ip | -| getDhcpInfo | ipConvertToString(Boolean, default=true) | Map | get dhcp info | -| getInternetInterfaceAddress | | Map | get Internet Interface Address | - ---- - -## BluetoothFacade - -| RPC Method | Parameters | Return | Description | -|------------|------------|--------|-------------| -| bluetoothActiveConnections | | Map | Returns active Bluetooth connections. | -| bluetoothWriteBinary | base64(String), connID(String, default="", optional) | void | Send bytes over the currently open Bluetooth connection. | -| bluetoothReadBinary | bufferSize(Integer, default=4096), connID(String, default="", optional) | String | Read up to bufferSize bytes and return a chunked, base64 encoded string. | -| bluetoothConnect | uuid(String, default=DEFAULT_UUID), address(optional) | String | Connect to a device over Bluetooth. | -| bluetoothAccept | uuid(String, default=DEFAULT_UUID), timeout(Integer, default=0) | String | Listens for and accepts a Bluetooth connection. | -| bluetoothMakeDiscoverable | duration(Integer, default=300) | void | Requests that the device be discoverable for Bluetooth connections. | -| bluetoothWrite | ascii(String), connID(String, default="") | void | Sends ASCII characters over the currently open Bluetooth connection. | -| bluetoothReadReady | connID(String, default="", optional) | Boolean | Returns True if the next read is guaranteed not to block. | -| bluetoothRead | bufferSize(Integer, default=4096), connID(String, default="", optional) | String | Read up to bufferSize ASCII characters. | -| bluetoothReadLine | connID(String, default="", optional) | String | Read the next line. | -| bluetoothGetRemoteDeviceName | address(String) | String | Queries a remote device for it's name or null if it can't be resolved | -| bluetoothGetBondedDevices | | JSONObject | bluetooth Get Bonded Devices | -| bluetoothGetBondedDevicesRssi | interval(optional) | JSONObject | bluetooth Get Bonded Devices Rssi | -| bluetoothGetReceivedDevices | | JSONObject | bluetooth Get Received Devices | -| bluetoothGetLocalName | | String | Gets the Bluetooth Visible device name | -| bluetoothSetLocalName | name(String) | boolean | Sets the Bluetooth Visible device name, returns True on success | -| bluetoothGetScanMode | | int | Gets the scan mode for the local dongle. | -| bluetoothGetConnectedDeviceName | connID(String, default="", optional) | String | Returns the name of the connected device. | -| checkBluetoothState | | Boolean | Checks Bluetooth state. | -| toggleBluetoothState | enabled(optional), prompt(Boolean, default=true) | Boolean | Toggle Bluetooth on and off. | -| bluetoothStop | connID(String, default="", optional) | void | Stops Bluetooth connection. | -| bluetoothGetLocalAddress | | String | Returns the hardware address of the local Bluetooth adapter. | -| bluetoothDiscoveryStart | | Boolean | Start the remote device discovery process. | -| bluetoothDiscoveryCancel | | Boolean | Cancel the current device discovery process. | -| bluetoothIsDiscovering | | Boolean | Return true if the local Bluetooth adapter is currently in the device discovery process. | - ---- - -## ContactsFacade - -| RPC Method | Parameters | Return | Description | -|------------|------------|--------|-------------| -| pickContact | | Intent | Display a list of contacts to pick from. | -| pickPhone | | String | Display a list of phone numbers to pick from. | -| contactsGetAttributes | | List | Returns a List of all possible attributes for contacts. | -| contactsGetIds | | List | Returns a List of all contact IDs. | -| contactsGet | attributes(optional) | List | Returns a List of all contacts. | -| contactsGetById | id(Integer), attributes(optional) | JSONObject | Returns contacts by ID. | -| contactsGetCount | | Integer | Returns the number of contacts. | -| queryContent | uri(String), attributes(optional), selection(optional), selectionArgs(optional), order(optional) | List | Content Resolver Query | -| queryAttributes | uri(String) | JSONArray | Content Resolver Query Attributes | - ---- - -## SettingsFacade - -| RPC Method | Parameters | Return | Description | -|------------|------------|--------|-------------| -| setScreenTimeout | value(Integer) | Integer | Sets the screen timeout to this number of seconds. | -| getScreenTimeout | | Integer | Returns the current screen timeout in seconds. | -| checkAirplaneMode | | Boolean | Checks the airplane mode setting. | -| checkRingerSilentMode | | Boolean | Checks the ringer silent mode setting. | -| toggleRingerSilentMode | enabled(optional) | Boolean | Toggles ringer silent mode on and off. | -| toggleVibrateMode | enabled(optional), ringer(optional) | Boolean | Toggles vibrate mode on and off. | -| getVibrateMode | ringer(optional) | Boolean | Checks Vibration setting. | -| getMaxRingerVolume | | int | Returns the maximum ringer volume. | -| getRingerVolume | | int | Returns the current ringer volume. | -| setRingerVolume | volume(Integer) | void | Sets the ringer volume. | -| getMaxMediaVolume | | int | Returns the maximum media volume. | -| getMediaVolume | | int | Returns the current media volume. | -| setMediaVolume | volume(Integer) | void | Sets the media volume. | -| getScreenBrightness | | Integer | Returns the screen backlight brightness. | -| setScreenBrightness | value(optional) | Integer | Sets the the screen backlight brightness. | -| checkScreenOn | | Boolean | Checks if the screen is on or off. | -| isExternalStorageManager | | Boolean | return isExternalStorageManager if Android >= 11. | -| getLocale | | String | Get system language and country. (requires API N) | -| getSysInfo | | Map | get system infomation. | -| getAndroidID | | String | get Android ID | -| getScreenInfo | | Map | get screen infomation. | -| elapsedRealtimeNanos | | Long | Nanoseconds after system startup | -| showScreenLock | | Boolean | Show Screen Lock. (requires API M) | -| getTrafficStats | flags(Integer, default=7) | Map | Get transmit/receive traffic statistics since startup. | -| getAppTxBytes | packageName(String) | Map | get qpython Tx bytes | -| getMemoryInfo | | Map | get Memory Information | - ---- - -## SpeechRecognitionFacade - -| RPC Method | Parameters | Return | Description | -|------------|------------|--------|-------------| -| recognizeSpeech | prompt(optional), language(optional), languageModel(optional) | String | Recognizes user's speech and returns the most likely result. | - ---- - -## TextToSpeechFacade (@RpcMinSdk(4)) - -| RPC Method | Parameters | Return | Description | -|------------|------------|--------|-------------| -| ttsSpeak | message(String), pitch(Double, default=1.0), pitchRate(Double, default=1.0) | void | Speaks the provided message via TTS. | -| ttsIsSpeaking | | Boolean | Returns True if speech is currently in progress. | -| ttsStop | | void | Stop speaking TTS. | - ---- - -## ToneGeneratorFacade - -| RPC Method | Parameters | Return | Description | -|------------|------------|--------|-------------| -| generateDtmfTones | phoneNumber(String), toneDuration(Integer, default=100) | void | Generate DTMF tones for the given phone number. | - ---- - -## UiConfig - -| RPC Method | Parameters | Return | Description | -|------------|------------|--------|-------------| -| htmlPictureSetSize | widthFixed(optional), heightFixed(optional), widthRatio(optional), heightRatio(optional) | void | Set html picture size. widthFixed or heightFixed = 0 means original picture size. | -| htmlPictureGetSize | | Map | Get html picture size. Returns widthFixed, heightFixed, widthRatio, heightRatio. | - ---- - -## UiFacade - -| RPC Method | Parameters | Return | Description | -|------------|------------|--------|-------------| -| dialogCreateInput | title(String, default="Value"), message(String, default="Please enter value:"), defaultText(optional), inputType(optional) | void | Create a text input dialog. | -| dialogCreatePassword | title(String, default="Password"), message(String, default="Please enter password:") | void | Create a password input dialog. | -| dialogSetMessageIsHtml | messageIsHtml(Boolean, default=true) | void | Set dialog message is HTML. | -| dialogGetInput | title(String, default="Value"), message(String, default="Please enter value:"), defaultText(optional), messageIsHtml(Boolean, default=false) | String | Queries the user for a text input. | -| dialogGetPassword | title(String, default="Password"), message(String, default="Please enter password:") | String | Queries the user for a password. | -| dialogCreateSpinnerProgress | title(optional), message(optional), maximum progress(Integer, default=100) | void | Create a spinner progress dialog. | -| dialogCreateHorizontalProgress | title(optional), message(optional), maximum progress(Integer, default=100) | void | Create a horizontal progress dialog. | -| dialogCreateAlert | title(optional), message(optional) | void | Create alert dialog. | -| dialogShowAlert | title(String, default="Alert"), message(String, default="The message of the alert ."), positive(String, default="OK"), negative(optional), neutral(optional), messageIsHtml(Boolean, default=false) | Object | Create and show alert dialog. | -| dialogCreateSeekBar | starting value(Integer, default=50), maximum value(Integer, default=100), title(String), message(String) | void | Create seek bar dialog. | -| dialogCreateTimePicker | hour(Integer, default=0), minute(Integer, default=0), is24hour(Boolean, default=false) | void | Create time picker dialog. | -| dialogCreateDatePicker | year(Integer, default=1970), month(Integer, default=1), day(Integer, default=1) | void | Create date picker dialog. | -| dialogDismiss | | void | Dismiss dialog. | -| dialogShow | | void | Show dialog. | -| dialogSetCurrentProgress | current(Integer) | void | Set progress dialog current value. | -| dialogSetMaxProgress | max(Integer) | void | Set progress dialog maximum value. | -| dialogSetProgressMessage | message(String) | void | Set progress dialog message. | -| dialogSetPositiveButtonText | text(String) | void | Set alert dialog positive button text. | -| dialogSetNegativeButtonText | text(String) | void | Set alert dialog button text. | -| dialogSetNeutralButtonText | text(String) | void | Set alert dialog button text. | -| dialogSetItems | items(JSONArray) | void | Set alert dialog list items. | -| dialogShowSimpleChoice | title(String, default="Alert"), message(String, default="The message of the alert ."), items(optional), positive(String, default="OK"), negative(optional), neutral(optional), messageIsHtml(Boolean, default=false) | Object | Create and show simple choice dialog. | -| dialogSetSingleChoiceItems | items(JSONArray), selected(Integer, default=-1) | void | Set dialog single choice items and selected item. | -| dialogShowSingleChoice | title(String, default="Alert"), message(String, default="The message of the alert ."), items(JSONArray), selected(Integer, default=-1), positive(String, default="OK"), negative(optional), neutral(optional), messageIsHtml(Boolean, default=false) | JSONObject | Create and show single choice dialog. | -| dialogSetMultiChoiceItems | items(JSONArray), selected(optional) | void | Set dialog multiple choice items and selection. | -| dialogShowMultiChoice | title(String, default="Alert"), message(String, default="The message of the alert ."), items(JSONArray), selected(optional), positive(String, default="OK"), negative(optional), neutral(optional), messageIsHtml(Boolean, default=false) | Object | Create and show multi choice dialog. | -| dialogGetResponse | | Object | Returns dialog response. | -| dialogGetSelectedItems | | Set | Returns list of items user selected. | -| fullShow | layout(String), title(optional), theme(optional) | List | Show Full Screen. | -| fullDismiss | | void | Dismiss Full Screen. | -| fullQuery | | Map> | Get Fullscreen Properties. | -| fullQueryDetail | id(String) | Map | Get fullscreen properties for a specific widget. | -| fullGetProperty | id(String), property(String) | String | Get a fullscreen property for a specific widget. | -| fullGetProperties | ids(JSONArray), property(String) | JSONArray | Get a fullscreen property for many specific widgets. | -| fullSetProperty | id(String), property(String), value(String) | String | Set a fullscreen widget's property. | -| fullSetProperties | ids(JSONArray), property(String), value(String) | String | Set many fullscreen widgets' property. | -| fullSetList | id(String), list(JSONArray), isHtml(Boolean, default=false), listType(Integer, default=0) | String | Attach a text/html/mixed list to a fullscreen widget. | -| fullSetListSelected | id(String), selected(Object) | String | Attach a html list to a fullscreen widget. | -| fullGetListSelected | id(String) | Object | Attach a multi choice list to a fullscreen widget. | -| fullSetList2 | id(String), list(JSONArray), intRes(JSONArray) | String | Attach a 2-line list to a fullscreen widget. | -| fullSetListHtml | id(String), list(JSONArray) | String | Attach a html list to a fullscreen widget. (**Deprecated**) | -| fullSetTitle | title(String) | void | Set the Full Screen Activity Title. | -| fullKeyOverride | keycodes(JSONArray), enable(Boolean, default=true) | JSONArray | Override default key actions. | -| fullGetScreenShot | path(optional) | String | Get the Full Screen Activity ScreenShot to path. | - ---- - -## WakeLockFacade - -| RPC Method | Parameters | Return | Description | -|------------|------------|--------|-------------| -| wakeLockAcquireFull | | void | Acquires a full wake lock (CPU on, screen bright, keyboard bright). | -| wakeLockAcquirePartial | | void | Acquires a partial wake lock (CPU on). | -| wakeLockAcquireBright | | void | Acquires a bright wake lock (CPU on, screen bright). | -| wakeLockAcquireDim | | void | Acquires a dim wake lock (CPU on, screen dim). | -| wakeLockRelease | | void | Releases the wake lock. | - ---- - -## BatteryManagerFacade - -| RPC Method | Parameters | Return | Description | -|------------|------------|--------|-------------| -| batteryGetCharge | | Integer | Returns the most recently received battery charge counter uAh. | -| readBatteryData | | Bundle | Returns the most recently recorded battery data. (throws "battery" events) | -| batteryStartMonitoring | | void | Starts tracking battery state. (starts event "battery") | -| batteryStopMonitoring | | void | Stops tracking battery state. (stops event "battery") | -| batteryGetStatus | | Integer | Returns the most recently received battery status data | -| batteryGetHealth | | Integer | Returns the most recently received battery health data | -| batteryGetPlugType | | Integer | Returns the most recently received plug type data | -| batteryCheckPresent | | Boolean | Returns the most recently received battery presence data. | -| batteryGetLevel | | Float | Returns the most recently received battery level (percentage). | -| batteryGetVoltage | | Integer | Returns the most recently received battery voltage. | -| batteryGetCurrent | | Integer | Returns the most recently received battery Current mA. | -| batteryGetTemperature | | Integer | Returns the most recently received battery temperature. | -| batteryGetTechnology | | String | Returns the most recently received battery technology data. | - ---- - -## SignalStrengthFacade - -| RPC Method | Parameters | Return | Description | -|------------|------------|--------|-------------| -| getTelephoneSignalStrengthLevel | | int | Returns the Telephone Signal Strength Level. (requires API P) | -| getTelephoneSignalStrengthDetail | | String | Returns the Telephone Signal Strength Detail. (requires API P) | -| startTrackingSignalStrengths | | void | Starts tracking signal strengths. GSM/CDMA/EVDO (starts event "signal_strengths") | -| readSignalStrengths | | Bundle | Returns the current signal strengths. GSM/CDMA/EVDO | -| stopTrackingSignalStrengths | | void | Stops tracking signal strength. GSM/CDMA/EVDO (stops event "signal_strengths") | - ---- - -## ApplicationManagerFacade - -| RPC Method | Parameters | Return | Description | -|------------|------------|--------|-------------| -| getLaunchablePackages | needClassName(Boolean, default=false) | Map | Returns a list of all launchable packages with class name and application name. | -| getApplicationInfo | packageName(optional) | JSONObject | get Application Info | -| launch | classname(optional), packagename(optional), wait(Boolean, default=true) | void | Start activity with the given classname and/or packagename. | -| getRunningPackages | | Set | Returns a list of packages running activities or services. | -| getInstalledPackages | flag(Integer, default=4) | Map | get installed packages | -| forceStopPackage | packageName(String) | void | Force stops a package. | -| checkPermissions | | JSONObject | check all Permissions are granted or denied. | -| requestPermissions | permissions(optional) | void | request Permissions | - ---- - -## CommonIntentsFacade - -| RPC Method | Parameters | Return | Description | -|------------|------------|--------|-------------| -| pick | uri(String) | Intent | Display content to be picked by URI (e.g. contacts) | -| scanBarcode | title(optional) | String | Starts the barcode scanner. | -| scanBarcodeFromImage | path(String), sampleSize(Integer, default=0), x(Integer, default=0), y(Integer, default=0), width(Integer, default=0), height(Integer, default=0) | String | scan Barcode From Image | -| view | uri(String), type(optional), extras(optional), wait(Boolean, default=true) | void | Start activity with view action by URI | -| send | uri(String), type(optional), extras(optional), wait(Boolean, default=true) | void | Start activity with send action by URI | -| sendText | text(String), extras(optional), wait(Boolean, default=true) | void | Start activity with send action by text. | -| sendEmail | to(String), subject(String), text(String), path(optional), extras(optional), wait(Boolean, default=true) | void | Launches an activity that sends an e-mail message with a given recipients or attachment. | -| pathToUri | path(String), fileProvider(Boolean, default=true) | String | Convert normal path to content:// . | -| openFile | path(String), type(optional), wait(Boolean, default=true) | void | Open a file with path | -| sendFile | path(Object), type(optional), extras(optional), wait(Boolean, default=true) | void | Send file(s) with path | -| getPathType | path(String) | String | get path type | -| viewMap | query(String), wait(Boolean, default=true) | void | Opens a map search for query | -| viewContacts | wait(optional) | void | Opens the list of contacts. | -| search | query(String) | void | Starts a search for the given query. | -| viewHtml | path(String), title(optional), wait(Boolean, default=true) | void | Opens the browser to display a local HTML/text/audio/video File or http(s) Website. | -| webViewShow | path(String), title(optional), wait(Boolean, default=true) | void | Opens the browser to display a local HTML/text/audio/video File or http(s) Website. (**Deprecated**) | -| videoPlay | path(String), wait(Boolean, default=true) | void | Play the Video via Video Path. | -| editorOpen | path(String) | void | Opens the QEditor to display a local text File. | -| createScriptShortCut | scriptPath(String), label(optional), iconPath(optional), scriptArg(optional) | void | create python script shortcut. | - ---- - -## PreferencesFacade - -| RPC Method | Parameters | Return | Description | -|------------|------------|--------|-------------| -| prefGetValue | key(String), filename(optional) | Object | Read a value from shared preferences | -| prefPutValue | key(String), value(Object), filename(optional) | void | Write a value to shared preferences | -| prefGetAll | filename(optional) | Map | Get list of Shared Preference Values | -| prefRemoveValue | key(String), filename(optional) | void | Remove a value from shared preferences | - ---- - -## DocumentFileFacade - -| RPC Method | Parameters | Return | Description | -|------------|------------|--------|-------------| -| documentTreeShowOpen | rootPath(String) | Uri | Show Open Document Tree with RootPath. | -| documentFileRenameTo | src(String), dest(String) | boolean | Document File Rename. | -| documentFileDelete | file(String) | boolean | Document File ( or Tree ) Delete. | -| documentFileMkdir | dir(String) | boolean | Document File Make Directorys. | -| documentFileInputStream | srcFile(String), encodingFormat(String, default=""), skip(optional), length(optional) | String | Document File Input Stream. | -| documentFileOutputStream | destFile(String), srcString(String, default=""), encodingFormat(String, default=""), append(optional) | void | Document File Output Stream. | -| documentFileCopy | src(String), dest(String) | void | Document File Copy. | -| documentFileListFiles | folder(String) | JSONArray | Document File List Files. | -| documentFileShowOpen | rootPath(String) | Uri | Show Open Document Tree with RootPath. (**Deprecated**) | -| documentFileMoveTo | src(String), dest(String) | boolean | Document File Move To. (**Deprecated**) | -| documentFileMkdirs | dir(String) | boolean | Document File Make Directorys. (**Deprecated**) | -| documentFileReadFrom | srcFile(String), encodingFormat(String, default=""), skip(optional), length(optional) | String | Document File Read From. (**Deprecated**) | -| documentFileWriteTo | destFile(String), srcString(String, default=""), encodingFormat(String, default=""), append(optional) | void | Document File Write To. (**Deprecated**) | -| documentFileGetUri | path(String), isDirectory(optional) | Uri | Document File Get Uri. | -| documentFileIsDirectory | path(String) | Boolean | Document File Is Directory. | -| documentFileIsFile | path(String) | Boolean | Document File Is File. | -| documentFileExists | path(String) | Boolean | Document File Exists. | -| documentFileLength | path(String) | Long | Document File Length. | -| documentFileLastModified | path(String) | Long | Document File Last Modified. | -| documentFileGetStat | path(String) | Map | Document File Get Stat. | -| getFileStat | path(String) | Map | get file stat. | -| getSdCardPaths | | String[] | get Sd Card Paths. | - ---- - -## FloatViewFacade - -| RPC Method | Parameters | Return | Description | -|------------|------------|--------|-------------| -| floatView | args(optional) | int | Show Float View. | -| floatViewResult | index(Integer, default=-1) | JSONObject | Return Float View Result. | -| floatViewRemove | index(Integer, default=-1) | int | Remove Float View. | -| floatViewCount | | int | Float View Count. | -| backgroundProtect | enabled(Boolean, default=true) | void | QPython Background Protect. | - ---- - -## AccessibilityFacade - -| RPC Method | Parameters | Return | Description | -|------------|------------|--------|-------------| -| accessibilityStartService | | boolean | Opens system settings to enable accessibility service. | -| accessibilityClick | x(Double, default=0), y(Double, default=0), t(Integer, default=50) | Boolean | Performs a click on the screen at the specified coordinates. | -| accessibilitySlide | XnYn(optional), t(optional) | Boolean | Performs a slide on the screen at the specified coordinates. | -| accessibilityAction | actionCode(Integer, default=0) | Boolean | Performs a accessibility action with the designated action code. | -| accessibilityServiceEnabled | | boolean | Check if accessibility service is enabled. | - ---- - -## ActivityResultFacade - -| RPC Method | Parameters | Return | Description | -|------------|------------|--------|-------------| -| setResultBoolean | resultCode(Integer), resultValue(Boolean) | void | Sets the result of a script execution. | -| setResultByte | resultCode(Integer), resultValue(Integer) | void | Sets the result of a script execution. | -| setResultShort | resultCode(Integer), resultValue(Integer) | void | Sets the result of a script execution. | -| setResultChar | resultCode(Integer), resultValue(String) | void | Sets the result of a script execution. | -| setResultInteger | resultCode(Integer), resultValue(Integer) | void | Sets the result of a script execution. | -| setResultLong | resultCode(Integer), resultValue(Long) | void | Sets the result of a script execution. | -| setResultFloat | resultCode(Integer), resultValue(Double) | void | Sets the result of a script execution. | -| setResultDouble | resultCode(Integer), resultValue(Double) | void | Sets the result of a script execution. | -| setResultString | resultCode(Integer), resultValue(String) | void | Sets the result of a script execution. | -| setResultBooleanArray | resultCode(Integer), resultValue(Boolean[]) | void | Sets the result of a script execution. | -| setResultByteArray | resultCode(Integer), resultValue(Integer[]) | void | Sets the result of a script execution. | -| setResultShortArray | resultCode(Integer), resultValue(Integer[]) | void | Sets the result of a script execution. | -| setResultCharArray | resultCode(Integer), resultValue(String[]) | void | Sets the result of a script execution. | -| setResultIntegerArray | resultCode(Integer), resultValue(Integer[]) | void | Sets the result of a script execution. | -| setResultLongArray | resultCode(Integer), resultValue(Long[]) | void | Sets the result of a script execution. | -| setResultFloatArray | resultCode(Integer), resultValue(Double[]) | void | Sets the result of a script execution. | -| setResultDoubleArray | resultCode(Integer), resultValue(Double[]) | void | Sets the result of a script execution. | -| setResultStringArray | resultCode(Integer), resultValue(String[]) | void | Sets the result of a script execution. | -| setResultSerializable | resultCode(Integer), resultValue(Serializable) | void | Sets the result of a script execution. | - ---- - -## CipherFacade - -| RPC Method | Parameters | Return | Description | -|------------|------------|--------|-------------| -| cipherInit | key(String), algorithm(String, default="AES/CBC/PKCS5Padding"), encodingFormat(String, default=""), initialVector(Object, default="") | void | Initialize Encrypt Engine / Decrypt Engine. | -| encryptString | srcString(String) | String | Encrypt a Normal / Base64 String to another Base64 String. | -| decryptString | srcString(String) | String | Decrypt a Base64 String to another Normal / Base64 String. | -| encryptStringToFile | srcString(String), dstFile(String) | void | Encrypt a Normal / Base64 String to another File. | -| decryptFileToString | srcFile(String) | String | Decrypt a File to another Normal / Base64 String. | -| encryptFile | srcFile(String), dstFile(String) | void | Encrypt a File to another File. | -| decryptFile | srcFile(String), dstFile(String) | void | Decrypt a File to another File. | - ---- - -## FtpFacade - -| RPC Method | Parameters | Return | Description | -|------------|------------|--------|-------------| -| ftpStart | | String[] | FTP Start Service. | -| ftpStop | | void | FTP Stop Service. | -| ftpIsRunning | | boolean | FTP Server is Running. | -| ftpGet | | String[] | FTP Server get IP address. | -| ftpSet | port(optional), rootDir(optional), username(optional), password(optional) | JSONObject | FTP Server set port,rootDir,username,password. | -| ftpStatus | | String | FTP Server get status. | - ---- - -## HarmonyOsFacade - -| RPC Method | Parameters | Return | Description | -|------------|------------|--------|-------------| -| getSystemProperty | property(String), defaultValue(String, default="") | String | get System Property | -| getHarmonyOsInformation | | Map | get Harmony OS Information. | - ---- - -## QPyInterfaceFacade - -| RPC Method | Parameters | Return | Description | -|------------|------------|--------|-------------| -| executeQPyAsSrv | path(optional) | Boolean | Execute QPython script throught SL4A | -| executeQPy | path(String, default=""), arg(optional) | Boolean | Execute QPython script throught SL4A | -| executeQPyCodeAsSrv | code(optional) | Boolean | Execute QPython script throught SL4A | -| executeQPyCode | code(optional) | Boolean | Execute QPython script throught SL4A | -| getLastLog | logName(String, default="last.log") | String | Get last QPython execute log | -| sharedVariableSet | key(String), value(String) | String | set Java Shared Variable. | -| sharedVariableGet | key(String) | String | get Java Shared Variable. | -| sharedVariableRemove | key(String) | String | remove Java Shared Variable. | - ---- - -## WebCamFacade (@RpcMinSdk(8)) - -| RPC Method | Parameters | Return | Description | -|------------|------------|--------|-------------| -| webcamStart | resolutionLevel(Integer, default=0), jpegQuality(Integer, default=20), port(Integer, default=0) | Object | Starts an MJPEG stream and returns a Tuple of address and port for the stream. | -| webcamAdjustQuality | resolutionLevel(Integer, default=0), jpegQuality(Integer, default=20) | void | Adjusts the quality of the webcam stream while it is running. | -| webcamStop | | void | Stops the webcam stream. | -| cameraStartPreview | resolutionLevel(Integer, default=0), jpegQuality(Integer, default=20), filepath(optional) | boolean | Start Preview Mode. Throws 'preview' events. | -| cameraStopPreview | | void | Stop the preview mode. | - ---- - -## USBHostSerialFacade (@RpcMinSdk(12)) - -| RPC Method | Parameters | Return | Description | -|------------|------------|--------|-------------| -| usbserialGetDeviceList | | Map | Returns USB devices reported by USB Host API. | -| usbserialDisconnect | connID(String, default="", optional) | void | Disconnect all USB-device. | -| usbserialActiveConnections | | Map | Returns active USB-device connections. | -| usbserialWriteBinary | base64(String), connID(String, default="", optional) | void | Send bytes over the currently open USB Serial connection. | -| usbserialReadBinary | bufferSize(Integer, default=4096), connID(String, default="", optional) | String | Read up to bufferSize bytes and return a chunked, base64 encoded string. | -| usbserialConnect | hash(String, default=DEFAULT_HASHCODE), options(String, default="") | String | Connect to a device with USB-Host. | -| usbserialHostEnable | | Boolean | Requests that the host be enable for USB Serial connections. | -| getUsbDeviceType | hash(String, default=DEFAULT_HASHCODE) | String | Requests that the type of the device. | -| usbserialWrite | ascii(String), connID(String, default="") | void | Sends ASCII characters over the currently open USB Serial connection. | -| usbserialReadReady | connID(String, default="", optional) | Boolean | Returns True if the next read is guaranteed not to block. | -| usbserialRead | connID(String, default=""), bufferSize(Integer, default=4096, optional) | String | Read up to bufferSize ASCII characters. | -| usbserialGetDeviceName | connID(String, default="", optional) | String | Queries a remote device for it's name or null if it can't be resolved | - ---- - -## Summary - -| Facade Class | Number of RPC Methods | -|--------------|----------------------| -| AndroidFacade | 19 | -| EventFacade | 13 | -| LocationFacade | 8 | -| SensorManagerFacade | 11 | -| PhoneFacade | 29 | -| SmsFacade | 8 | -| CameraFacade | 4 | -| MediaRecorderFacade | 12 | -| MediaPlayerFacade | 10 | -| WifiFacade | 15 | -| BluetoothFacade | 25 | -| ContactsFacade | 9 | -| SettingsFacade | 26 | -| SpeechRecognitionFacade | 1 | -| TextToSpeechFacade | 3 | -| ToneGeneratorFacade | 1 | -| WakeLockFacade | 5 | -| BatteryManagerFacade | 13 | -| SignalStrengthFacade | 5 | -| ApplicationManagerFacade | 8 | -| CommonIntentsFacade | 19 | -| PreferencesFacade | 4 | -| DocumentFileFacade | 22 | -| FloatViewFacade | 5 | -| AccessibilityFacade | 5 | -| ActivityResultFacade | 19 | -| CipherFacade | 7 | -| FtpFacade | 6 | -| HarmonyOsFacade | 2 | -| QPyInterfaceFacade | 8 | -| WebCamFacade | 5 | -| USBHostSerialFacade | 12 | - -**Total: 32 Facade Classes with 344 RPC Methods** \ No newline at end of file diff --git a/qsl4a-src/android.py b/qsl4a-src/android.py deleted file mode 100644 index bd454db..0000000 --- a/qsl4a-src/android.py +++ /dev/null @@ -1,46 +0,0 @@ -#简易 SL4A 服务 : 快速 但 单实例 功能限制 -#by 乘着船 2021-2024 - -import os,socket as s - -k=os.environ.get -c=s.create_connection((k('AP_HOST'),k('AP_PORT'))).makefile("rw") - -import collections -from json import dumps,loads -i=0 -w,s,c=c.write,c.flush,c.readline - -def jsla(method,*params): - global i - w(dumps({'id':i, - 'method':method,'params':params})+'\n') - s() - i+=1 - return c() - -jsla('_authenticate',k('AP_HANDSHAKE')) -k=collections.namedtuple('Result','id,result,error') - -def rsla(*a): - return loads(jsla(*a))['result'] - -def esla(*a): - r=loads(jsla(*a)) - if r['error']: - raise Exception(r['error']) - else: - return r['result'] - -def nsla(*a): - return k(**loads(jsla(*a))) - -class Android: - def __getattr__(self,method): - def f(*params): - return nsla(method,*params) - setattr(self,method,f) - return f -droid=Android() - -__all__=('droid','esla','rsla','jsla','os') \ No newline at end of file diff --git a/qsl4a-src/androidhelper/Intent.py b/qsl4a-src/androidhelper/Intent.py deleted file mode 100644 index 5150bc9..0000000 --- a/qsl4a-src/androidhelper/Intent.py +++ /dev/null @@ -1,56 +0,0 @@ -'Common Intent Constants , for more Intent Constants, please refer https://developer.android.google.cn/reference/android/content/Intent.html .\n\nYou can use them like droid.startActivityIntent(droid.makeIntent(action=droid.Intent.ACTION_VIEW,uri="http://www.baidu.com",flags=droid.Intent.FLAG_ACTIVITY_NEW_DOCUMENT).result) .' - -ACTION_MAIN='android.intent.action.MAIN' -ACTION_VIEW='android.intent.action.VIEW' -ACTION_EDIT='android.intent.action.EDIT' -ACTION_PICK='android.intent.action.PICK' -ACTION_CHOOSER='android.intent.action.CHOOSER' -ACTION_INSERT='android.intent.action.INSERT' -ACTION_DELETE='android.intent.action.DELETE' -ACTION_RUN='android.intent.action.RUN' -ACTION_SEND='android.intent.action.SEND' -ACTION_SYNC='android.intent.action.SYNC' -ACTION_SEARCH='android.intent.action.SEARCH' - -FLAG_ACTIVITY_SINGLE_TOP = 536870912 -FLAG_ACTIVITY_MULTIPLE_TASK = 134217728 -FLAG_ACTIVITY_NEW_DOCUMENT = 524288 -FLAG_ACTIVITY_CLEAR_TASK = 32768 -FLAG_ACTIVITY_NEW_TASK = 268435456 - -EXTRA_TEXT='android.intent.extra.TEXT' -EXTRA_STREAM='android.intent.extra.STREAM' -EXTRA_SUBJECT='android.intent.extra.SUBJECT' -EXTRA_EMAIL='android.intent.extra.EMAIL' -EXTRA_HTML_TEXT='android.intent.extra.HTML_TEXT' - -def _intentExtras(Extras): - if not isinstance(Extras,dict): - return Extras - for Key in Extras.keys(): - Val = Extras[Key] - if isinstance( Val ,( bytes, bytearray )): - Extras[Key]='\x00byte\x00'+_encode(Val).decode() - elif isinstance( Val, str ): - if Val[0:1]=='\x00': - Extras[Key]='\x00str\x00'+Val - elif isinstance( Extras, dict ): - Extras[Key]=_intentExtras(Val) - elif isinstance( Val, Uri ): - Extras[Key]='\x00uri\x00'+Val - return Extras - -class _uri(str): - def __new__(cls,arg): - HEAD='\x00uri\x00' - if arg[:5]!=HEAD: - arg=HEAD+arg - return super(_uri,cls).__new__(cls,arg) - def __repr__(self): - return 'Uri("'+self[5:]+'")' - def __str__(self): - return self[5:] - -from .__init__ import Android as _encode -_uri.__doc__=_encode.Uri.__doc__ -from base64 import b64encode as _encode \ No newline at end of file diff --git a/qsl4a-src/androidhelper/R/__init__.py b/qsl4a-src/androidhelper/R/__init__.py deleted file mode 100644 index a08320a..0000000 --- a/qsl4a-src/androidhelper/R/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -'android.R module, include id,layout,style Constants .' -class R: - @property - def id(self): - from . import id - R.id=id - return id - @property - def layout(self): - from . import layout - R.layout=layout - return layout - @property - def style(self): - from . import style - R.style=style - return style -R.__doc__=__doc__ \ No newline at end of file diff --git a/qsl4a-src/androidhelper/R/id.py b/qsl4a-src/androidhelper/R/id.py deleted file mode 100644 index a443222..0000000 --- a/qsl4a-src/androidhelper/R/id.py +++ /dev/null @@ -1,3 +0,0 @@ -'android.R.id Constants, for more id Constants, please refer https://developer.android.google.cn/reference/android/R.id.html .' -text1=16908308 -text2=16908309 diff --git a/qsl4a-src/androidhelper/R/layout.py b/qsl4a-src/androidhelper/R/layout.py deleted file mode 100644 index a36a768..0000000 --- a/qsl4a-src/androidhelper/R/layout.py +++ /dev/null @@ -1,7 +0,0 @@ -'android.R.layout Constants, for more layout Constants, please refer https://developer.android.google.cn/reference/android/R.layout.html .' -simple_list_item_1=17367043 -simple_list_item_2=17367044 -simple_expandable_list_item_1=17367046 -simple_expandable_list_item_2=17367047 -simple_spinner_item=17367048 -simple_spinner_dropdown_item=17367049 diff --git a/qsl4a-src/androidhelper/R/style.py b/qsl4a-src/androidhelper/R/style.py deleted file mode 100644 index 0c9cdfe..0000000 --- a/qsl4a-src/androidhelper/R/style.py +++ /dev/null @@ -1,20 +0,0 @@ -'android.R.style Constants, for more style Constants, please refer https://developer.android.google.cn/reference/android/R.style.html .' -Default=0 -Theme_Black=16973832 -Theme_Black_NoTitleBar=16973833 -Theme_Black_NoTitleBar_Fullscreen=16973834 -Theme_Light=16973836 -Theme_Light_NoTitleBar=16973837 -Theme_Light_NoTitleBar_Fullscreen=16973838 -Theme_Light_Panel=16973914 -Theme_Wallpaper=16973918 -Theme_Wallpaper_NoTitleBar=16973919 -Theme_Wallpaper_NoTitleBar_Fullscreen=16973920 -Theme_Holo=16973931 -Theme_Holo_Wallpaper=16973949 -Theme_DeviceDefault_Panel=16974138 -Theme_DeviceDefault_Wallpaper=16974140 -Theme_DeviceDefault_Wallpaper_NoTitleBar=16974141 -TextAppearance_DeviceDefault_Large_Inverse=16974256 -TextAppearance_DeviceDefault_Medium_Inverse=16974258 -Theme_DeviceDefault_Settings=16974371 diff --git a/qsl4a-src/androidhelper/README.htm b/qsl4a-src/androidhelper/README.htm deleted file mode 100644 index ff7b630..0000000 --- a/qsl4a-src/androidhelper/README.htm +++ /dev/null @@ -1,27 +0,0 @@ - -

SL4A = Scripting Layer for Android = 安卓脚本层

-

Original Author :
-原始作者 :
-Damon Kohler <damonkohler@gmail.com> at 2009

-

Original Source Code :
-原始源代码 :
-https://github.com/damonkohler/sl4a

-

QPython SL4A OpenSource Licence :
-QPython SL4A 开源协议 :
-SL4A-2026 Update by 乘着船 @ bilibili at 2021-2026 .
-SL4A-2026 更新 by 乘着船 @ bilibili at 2021-2026。
-The code updated by 乘着船 is a non-commercial license,
-乘着船 更新的代码部分,为非商业性许可,
-if there is a commercial need, which requires 乘着船's consent .
-如确有商用需求,需经 乘着船 同意,
-The rest of the code follows the Apache 2.0 open source license .
-其余部分才遵循 Apache 2.0 开源协议。

-

Apache 2.0 OpenSource Licence :
-Apache 2.0 开源协议 :
-http://www.apache.org/licenses/LICENSE-2.0

-

New version of SL4A website :
-新版SL4A网址 :
-https://gitee.com/buddygr/qpython-sl4a

-

乘着船 @ bilibili
-https://www.bilibili.com/space/9185070

- \ No newline at end of file diff --git a/qsl4a-src/androidhelper/__init__.py b/qsl4a-src/androidhelper/__init__.py deleted file mode 100644 index 1c86c67..0000000 --- a/qsl4a-src/androidhelper/__init__.py +++ /dev/null @@ -1,70 +0,0 @@ -from . import sl4a - -class Android(sl4a.Android): - _o=sl4a.os - _l=_o.listdir - _o=_o.path.isdir - _g=__file__[:-11] - - def _addMethod(_file): - exec(open(__file__[:-11]+_file+'.py').read()) - for _i in tuple(locals()): - if _i[0]=='_': - continue - exec(f"sl4a.Android.{_i}={_i}") - locals().clear() - - _f=_g+'fun/' - for _i in _l(_f): - _t=open(_f+_i).read() - exec(_t) - - _f=[_g+'doc/'] - while len(_f)>0: - _p=_f.pop(-1) - for _i in _l(_p): - _t=_p+_i - if _o(_t): - _f.append(_t+'/') - else: - try: - _t=open(_t).read() - exec(_i[:-4]+".__doc__=_t") - except:pass - - _f=_g+'const/' - for _i in _l(_f): - try: - _t=open(_f+_i).read().splitlines() - _i=_i[:-4] - _o=[eval(_i+".__doc__"),''] - if _o[0]==None: - _o[0]='' - for _t in _t: - _p=_i+"."+_t - exec(_p) - _o.append(_p) - _o='\n'.join(_o) - exec(_i+".__doc__=_o") - except:pass - - def _addMethod(_self,_file): - if '_'+_file in _self.__dict__: - return - exec(open(__file__[:-11]+_file+'.py').read()) - exec(f'_self._{_file}=None') - from types import MethodType as _m - for _i in tuple(locals()): - if _i[0]=='_': - continue - exec(f'_self.{_i}=_m({_i},_self)') - locals().clear() - - def _addClass(_self,_file): - if '_'+_file in _self.__dict__: - return - exec(f'_self._{_file}=None') - exec(f'from .{_file} import {_file}\n{_file}(_self)') - del _self,_file - - del _f,_i,_t,_l,_g,_o,_p \ No newline at end of file diff --git a/qsl4a-src/androidhelper/accessibility.py b/qsl4a-src/androidhelper/accessibility.py deleted file mode 100644 index cb1ff69..0000000 --- a/qsl4a-src/androidhelper/accessibility.py +++ /dev/null @@ -1,71 +0,0 @@ -_c=""" -StartService () -Start the Accessibility Service . -Return True if start successful . -Return False if start failed . - -ServiceEnabled () -Check if Accessibility Service is Enabled , -Return True or False . - -Click ( x=0, y=0, t=50 ) -Accessibility Click the Screen Point with the Coordinates ( x, y ) , -Default Coordinates ( 0, 0 ) means the Center of the Screen , -The Coordinates ( x, y ) can be Integers or Decimals such as 0.5 or -1.5 , -parameter Integer t is the Press Time for Milliseconds , default is 50 . - -Slide ( XnYn=None, t=None ) -Accessibility Slide the Screen with Multi Points , -XnYn is a Coordinate Set consisting of n points ( X1, Y1, X2, Y2, …… Xn, Yn ) , -Coordinates X=Y=0 means the Center of the Screen , -The Coordinates X and Y can be Integers or Decimals such as 0.5 or -1.5 , -parameter Integer t is the Slide Time for Milliseconds , default value None means 50*n ( 50n milliseconds ) . - -Action ( actionCode ) -Do Accessibility Action with the designated Action Code . -Return True for successful execution and False for failed execution . -Some action codes are not supported by certain models . -BACK = 1 -HOME = 2 -RECENTS = 3 -NOTIFICATIONS = 4 -QUICK_SETTINGS = 5 -POWER_DIALOG = 6 -TOGGLE_SPLIT_SCREEN = 7 -LOCK_SCREEN = 8 -TAKE_SCREENSHOT = 9 -KEYCODE_HEADSETHOOK = 10 -ACCESSIBILITY_BUTTON = 11 -ACCESSIBILITY_BUTTON_CHOOSER = 12 -ACCESSIBILITY_SHORTCUT = 13 -ACCESSIBILITY_ALL_APPS = 14 -DISMISS_NOTIFICATION_SHADE = 15 -DPAD_UP = 16 -DPAD_DOWN = 17 -DPAD_LEFT = 18 -DPAD_RIGHT = 19 -DPAD_CENTER = 20 -MENU = 21 -MEDIA_PLAY_PAUSE = 22 -""" - -for _c in _c[1:-1].split('\n\n'): - _c='accessibility'+_c - _e=_c.find(' (') - _d=_c[:_e] - _e=_c[_e+2:_c.find(')')] - _b=[] - for _a in _e.split(","): - _b.append(_a.split("=")[0]) - _b=",".join(_b) - exec(f''' -def {_d}(self,{_e}): - return self._rpc("{_d}",{_b}) -{_d}.__doc__=_c -''') - -for _e in _c.split("\n"): - if _e.find('=')>0: - exec(f"{_d}.{_e}") - -del _a,_b,_c,_d,_e diff --git a/qsl4a-src/androidhelper/battery.py b/qsl4a-src/androidhelper/battery.py deleted file mode 100644 index 31c17ee..0000000 --- a/qsl4a-src/androidhelper/battery.py +++ /dev/null @@ -1,53 +0,0 @@ -_doc_='''batteryStartMonitoring() - -batteryStopMonitoring() -Stops tracking battery state. - -batteryCheckPresent() -Returns the most recently received battery presence data. - -batteryGetHealth() -Returns the most recently received battery health data:1 - unknown;2 - good;3 - overheat;4 - dead;5 - over voltage;6 - unspecified failure; - -batteryGetLevel() -Returns the most recently received battery level (percentage). - -batteryGetPlugType() -Returns the most recently received plug type data: --1 - unknown; -0 - unplugged; -1 - power source is an AC charger; -2 - power source is a USB port - -batteryGetStatus() -Returns the most recently received battery status data:1 - unknown;2 - charging;3 - discharging;4 - not charging;5 - full; - -batteryGetTechnology() -Returns the most recently received battery technology data. - -batteryGetTemperature() -Returns the most recently received battery temperature. - -batteryGetVoltage() -Returns the most recently received battery voltage mV . - -readBatteryData() -Returns the most recently recorded battery data. - -batteryGetCurrent() -Returns the most recently received battery Current mA . - -batteryGetCharge() -Returns the most recently received battery Charge Counter μAh . -''' - -_g=_doc_.split('\n\n') - -for _t in _g: - _i=_t[:_t.find('(')] - exec(f'def {_i}(self):return self._rpc("{_i}")') - if _t.find('\n')==-1: - _t=eval(f'_self.{_i}.__doc__') - exec(f"{_i}.__doc__=_t") - -del _g,_i,_t,_doc_ diff --git a/qsl4a-src/androidhelper/bluetooth.py b/qsl4a-src/androidhelper/bluetooth.py deleted file mode 100644 index ab3b1be..0000000 --- a/qsl4a-src/androidhelper/bluetooth.py +++ /dev/null @@ -1,123 +0,0 @@ -_d=''' Include fields are name, bondState, type, uuid, connected, battery (if connected) . - type : 0 = Unknown , 1 = Classic , 2 = LowEnergy , 3 = Dual .''' -_d=f''' -Accept(uuid="457807c0-4897-11df-9879-0800200c9a66",timeout=0) -Listens for and accepts a Bluetooth connection. Blocks until the connection is established or fails. - uuid (String) (default=457807c0-4897-11df-9879-0800200c9a66) - timeout (Integer) How long to wait for a new connection, 0 is wait for ever (default=0) - -ActiveConnections() -Returns true when there's an active Bluetooth connection. - -Connect(uuid="457807c0-4897-11df-9879-0800200c9a66",address=None) -Connect to a device over Bluetooth. Blocks until the connection is established or fails. - uuid (String) The UUID passed here must match the UUID used by the server device. (default=457807c0-4897-11df-9879-0800200c9a66) - address (String) The user will be presented with a list of discovered devices to choose from if an address is not provided. (optional) -returns: (String) True if the connection was established successfully. - -GetConnectedDeviceName(connID=None) -Returns the name of the connected device. - connID (String) Connection id (optional) (default=) - -GetLocalName() -Gets the Bluetooth Visible device name - -GetRemoteDeviceName(address) -Queries a remote device for it's name or null if it can't be resolved - address (String) Bluetooth Address For Target Device - -GetScanMode() -Gets the scan mode for the local dongle. -Return values: - -1 when Bluetooth is disabled. - 0 if non discoverable and non connectable. - 1 connectable non discoverable. - 3 connectable and discoverable. - -MakeDiscoverable(duration=300) -Requests that the device be discoverable for Bluetooth connections. - duration (Integer) period of time, in seconds, during which the device should be discoverable (default=300) - -Read(bufferSize=4096,connID=None) -Read up to bufferSize ASCII characters. - bufferSize (Integer) (default=4096) - connID (String) Connection id (optional) (default=) - -ReadBinary(bufferSize=4096,connID=None) -Read up to bufferSize bytes and return a chunked, base64 encoded string. - bufferSize (Integer) (default=4096) - connID (String) Connection id (optional) (default=) - -ReadLine(connID=None) -Read the next line. - connID (String) Connection id (optional) (default=) - -ReadReady(connID=None) -Returns True if the next read is guaranteed not to block. - connID (String) Connection id (optional) (default=) - -SetLocalName(name) -Sets the Bluetooth Visible device name, returns True on success - name (String) New local name - -Stop(connID=None) -Stops Bluetooth connection. - connID (String) Connection id (optional) (default=) - -Write(ascii,connID="") -Sends ASCII characters over the currently open Bluetooth connection. - ascii (String) - connID (String) Connection id (default=) - -WriteBinary(base64,connID=None) -Send bytes over the currently open Bluetooth connection. - base64 (String) A base64 encoded String of the bytes to be sent. - connID (String) Connection id (optional) (default=) - -checkBluetoothState() - -toggleBluetoothState(enabled=None,prompt=True) - -DiscoveryStart() - Start Bluetooth Discovery - -DiscoveryCancel() - Cancel Bluetooth Discovery - -GetReceivedDevices() - Get received Bluetooth devices after droid.bluetoothDiscoveryStart() function -{_d} - -GetBondedDevices() - Get bonded devices , with address,name,type,bondState,uuid . - You need to open Bluetooth with droid.toggleBluetoothState(True) . - If you call droid.bluetoothGetBondedDevicesRssi(interval) with interval>0 first , it will return rssi of connected device at the same time . -{_d} - -GetBondedDevicesRssi(interval=None) - interval (Integer Optional) - If interval > 0 , start detect connected devices' Rssi, - if interval < 0 , stop detect connected devices' Rssi, - if interval == 0 or interval == None , return connected device's address and Rssi, or return unconnected devices' address , you need to call droid.bluetoothGetBondedDevicesRssi(interval) with interval>0 first . -''' - -for _p in _d[1:-1].split('\n\n'): - if _p[:_p.find('\n')].find('Bluetooth')==-1: - _d='bluetooth'+_p - else: - _q=_p[:_p.find('(')] - exec(f'_d=_self.{_q}.__doc__') - _f=_d[:_d.find("\n")] - _m,_o=_f.split('(',1) - _n=_m+'(self,'+_o - _q=[] - for _o in _o[:-1].split(','): - _q.append(_o.split('=',1)[0]) - _q=','.join(_q) - if _q: - _q=','+_q - exec(f'def {_n}:return self._rpc("{_m}"{_q})') - exec(f'{_m}.__doc__=_d') -del _p,_d,_q,_f,_m,_n,_o - -# Edit by 乘着船 at 2023-2024 diff --git a/qsl4a-src/androidhelper/cipher/__init__.py b/qsl4a-src/androidhelper/cipher/__init__.py deleted file mode 100644 index 8f17a3f..0000000 --- a/qsl4a-src/androidhelper/cipher/__init__.py +++ /dev/null @@ -1,74 +0,0 @@ -from json import dumps,loads -from base64 import b64encode as encode, b64decode as decode -from ..sl4a import Result -from ..__init__ import Android - -class cipher: - def __init__( this, sup ): - p=__file__[:__file__.rfind('/')+1] - for i in dir(this): - if i[0] != '_': - exec(f'sup.{i}=this.{i}') - exec(f'cipher.{i}.__doc__=open("{p}{i}.txt").read()') - this._rpc = sup._rpc - this._write = sup._write - this._flush = sup._flush - this._readline = sup._readline - this._sup = sup - i=p[:p.rfind('/c')+1] - cipher._init.__doc__ = open(f"{i}doc/cipherInit.txt").read().rsplit("\n",1)[0] + open(f"{p}_init.txt").read() - sup.cipherInit = this._init - - def encryptString( self, srcString ): - return self._rpc("encryptString", srcString) - - def decryptString( self, srcString ): - return self._rpc("decryptString", srcString) - - def encryptStringToFile( self, srcString , dstFile ): - return self._rpc("encryptStringToFile", srcString, dstFile) - - def decryptFileToString( self, srcFile ): - return self._rpc("decryptFileToString", srcFile) - - def encryptFile( self, srcFile, dstFile ): - return self._rpc("encryptFile", srcFile, dstFile) - - def decryptFile( self, srcFile, dstFile ): - return self._rpc("decryptFile", srcFile, dstFile) - - def encryptBytes( self, srcBytes ): - return self._brpc("encryptString", encode( srcBytes ).decode()) - - def decryptBytes( self, srcBytes ): - return self._brpc("decryptString", encode( srcBytes ).decode()) - - def encryptBytesToFile( self, srcBytes , dstFile ): - return self._rpc("encryptStringToFile", encode( srcBytes ).decode(), dstFile) - - def decryptFileToBytes( self, srcFile ): - return self._brpc("decryptFileToString", srcFile) - - def _brpc( self, method, *args ): - self._write( dumps({ - 'id': self._sup._id, - 'method': method, - 'params': args - }) + '\n' ) - self._flush() - self._sup._id += 1 - result = loads( self._readline() ) - if result['error'] is not None: - print(result['error']) - else: - result['result'] = decode( result['result'] ) - return Result( result['id'], result['result'], result['error'] ) - - def _init( self, key, algorithm = "AES/CBC/PKCS5Padding", encodingFormat = "", initialVector = ""): - if isinstance( key ,( bytes, bytearray )): - key = encode(key).decode() - if isinstance( initialVector ,( bytes, bytearray )): - initialVector = encode(initialVector).decode() - return self._rpc( "cipherInit", key, algorithm, encodingFormat, initialVector ) - -#by 乘着船 2021-2025 diff --git a/qsl4a-src/androidhelper/cipher/_init.txt b/qsl4a-src/androidhelper/cipher/_init.txt deleted file mode 100644 index 755a0d8..0000000 --- a/qsl4a-src/androidhelper/cipher/_init.txt +++ /dev/null @@ -1,117 +0,0 @@ - -#### Parameters #### - -### key : ( str or bytes ): ### - * The secret key used for encryption and decryption. - * If a `bytes` or `bytearray` object is provided, it is automatically converted to a **Base64-Encoded-String** before being sent to the backend. - * If `encodingFormat` is `""`, the string will be **Base64** directly . - * Otherwise, the string is converted to **Base64** using the specified `encodingFormat` (e.g., UTF-8, GBK). - * The key must be of a length appropriate for the chosen algorithm (e.g., 16, 24, or 32 bytes for AES , e.g., 8 bytes for DES). - -### algorithm : ( str , default: "AES/CBC/PKCS5Padding" ): ### - * Specifies the cipher algorithm, mode, and padding scheme in the format `Algorithm/Mode/Padding`. - * **Common Examples:** - * "AES/CBC/PKCS5Padding" (Default) - * "AES/GCM/NoPadding" - * "DES/CBC/PKCS5Padding" - * The algorithm determines the **suggested IV length**: - * `AES/GCM`: 12 bytes (96 bits) - *Recommended for GCM mode.* - * `DES`: 8 bytes. - * Other block cipher modes (e.g., CBC): 16 bytes. - * `ECB` mode: 0 bytes (IV not used). - -### encodingFormat : (str, default: ""): ### - * Specifies the character encoding used to convert string `key` and `initialVector` to bytes. - * An empty string (`""`) indicates that the `key` and `initialVector` are provided in **`bytes` or `base64`** format. - * Other valid values include "UTF-8", "GBK", etc., indicates that the `key` and `initialVector` are provided in "UTF-8", "GBK" format . - -### initialVector : ( str , bytes , or int , default: "" ): ### - * Controls the initialization vector (IV) behavior. The type of this parameter determines the mode: - ## str (String but **not empty**) - Use a Fixed IV (Legacy/Compatibility Mode): ## - * The string is interpreted as the IV value. - * If `encodingFormat` is `""`, the string will be decode to **Base64**. - * Otherwise, the string is converted to **Base64** using the specified `encodingFormat` (e.g., UTF-8, GBK). - * **Important:** The IV is **NOT** written to the output file or byte-string. This mode requires the IV to be known or shared out-of-band for decryption. - ## bytes (but **not empty**) - Use a Fixed IV (Legacy/Compatibility Mode): ## - * If `encodingFormat` is `""`, initialVector can be byte-string which will be decode to **Base64**. - ## int (Integer) - Generate a Random IV (New System Mode): ## - * The integer value specifies the `length (in bytes)` of the random IV to generate. - * A cryptographically secure random IV of the specified length is generated. - * **Important:** The **entire** generated IV is **prepended to the ciphertext** in the output file or byte-string. This is the secure and recommended method, as the IV is needed for decryption. - * The IV used for the cipher operation is derived from this random IV: - * If the generated IV's length is greater than the `SuggestIVLength` for the algorithm, only the **first `SuggestIVLength` bytes** are used as the actual cipher IV. - * The remaining bytes can serve as obfuscation or reserved space. - * **Example:** `initialVector=56` generates a 56-byte random sequence. The first 12 (for GCM) or 16 (for CBC) bytes are used as the cipher IV, and all 56 bytes are written to the output header. - ## `""` (Empty String - Default) - Generate a Random IV with Suggested Length: ## - * This is a convenience mode equivalent to passing `initialVector=SuggestIVLength`. - * A random IV of the **suggested length** for the chosen algorithm (e.g., 12 for GCM, 16 for CBC, 8 for DES) is generated. - * The **entire** IV is **prepended to the ciphertext** in the output. This is the recommended default for new applications. - -#### Return Value #### - - * If this function is run correct, it will return a **Result of the None** : - * Result(id=1, result=None, error=None) - -#### Example Usage #### - -# 1. Used a Bytes or Base64 Key (Simplest , IV is written to output, algorithm is default to AES/CBC/PKCS5Padding) : -```python ->>> from base64 import b64encode ->>> from androidhelper import * ->>> droid = Android() ->>> bs = b"it need 16 bytes" # bytes ->>> b64 = b64encode(bs).decode() # to base64 ->>> droid.cipherInit(b64) -Result(id=1, result=None, error=None) ->>> droid.cipherInit(bs) # the same as cipherInit(b64) -Result(id=2, result=None, error=None) -``` - -# 2. Use a fixed Bytes or Base64 Key and IV (Legacy, IV not written to output) -```python ->>> from androidhelper import * ->>> droid = Android() ->>> ks = b"it need 16 bytes" # bytes ->>> bs = b"it needs 16 byte" # bytes ->>> droid.cipherInit(key=ks, algorithm="AES/CBC/PKCS5Padding", encodingFormat="", initialVector=bs) -Result(id=1, result=None, error=None) -``` - -# 3. Use a fixed UTF-8 or GBK string as Key and IV (Legacy, IV not written to output) -```python ->>> from androidhelper import * ->>> droid = Android() ->>> ks = "key is 16 bytes." # string ->>> bs = "the iv 16 bytes." # string ->>> droid.cipherInit(key=ks, algorithm="AES/CBC/PKCS5Padding", encodingFormat="UTF-8", initialVector=bs) -Result(id=1, result=None, error=None) -``` - -# 4. Use a 256-bit-key (32 bytes, for AES-256), generate a random 16-byte IV and write it to the output (Recommended for CBC) -```python ->>> from androidhelper import * ->>> droid = Android() ->>> ks = "This key is 32 bytes for AES-256" # string ->>> droid.cipherInit(key=ks, algorithm="AES/CBC/PKCS5Padding", encodingFormat="GBK",initialVector=16) -Result(id=1, result=None, error=None) -``` - -# 5. Generate a random 12-byte IV (suggested for GCM) and write it to the output (Recommended for GCM) -```python ->>> from androidhelper import * ->>> droid = Android() ->>> ks = b"This key is 32 bytes for AES-256" # bytes ->>> droid.cipherInit(key=ks, algorithm="AES/GCM/NoPadding", encodingFormat="", initialVector=12) -Result(id=1, result=None, error=None) -``` - -# 6. Generate a random IV of suggested length and write it to the output -```python ->>> from androidhelper import * ->>> droid = Android() ->>> ks = b"key is 16 bytes." # bytes ->>> droid.cipherInit(key=ks, algorithm="AES/GCM/NoPadding", encodingFormat="", initialVector="") # Uses random 12-byte IV -Result(id=1, result=None, error=None) ->>> droid.cipherInit(key=ks, algorithm="AES/CBC/PKCS5Padding", encodingFormat="", initialVector="") # Uses random 16-byte IV -Result(id=2, result=None, error=None) -``` \ No newline at end of file diff --git a/qsl4a-src/androidhelper/cipher/decryptBytes.txt b/qsl4a-src/androidhelper/cipher/decryptBytes.txt deleted file mode 100644 index 95eefe4..0000000 --- a/qsl4a-src/androidhelper/cipher/decryptBytes.txt +++ /dev/null @@ -1,5 +0,0 @@ -decryptBytes( srcBytes ) -Decrypt a Bytes to another Bytes, -needs to run droid.cipherInit first , and encodingFormat must omitted . -srcBytes (Bytes) -return Decrypted Bytes diff --git a/qsl4a-src/androidhelper/cipher/decryptFile.txt b/qsl4a-src/androidhelper/cipher/decryptFile.txt deleted file mode 100644 index 6dec590..0000000 --- a/qsl4a-src/androidhelper/cipher/decryptFile.txt +++ /dev/null @@ -1,7 +0,0 @@ -decryptFile( srcFile, dstFile ) -Decrypt a File to another File, -needs to run droid.cipherInit first . -if you want to operate files in Media Storage Device or /sdcard/Android/data/ , run droid.documentTreeShowOpen first . -srcFile (String) -dstFile (String) -return None diff --git a/qsl4a-src/androidhelper/cipher/decryptFileToBytes.txt b/qsl4a-src/androidhelper/cipher/decryptFileToBytes.txt deleted file mode 100644 index e863351..0000000 --- a/qsl4a-src/androidhelper/cipher/decryptFileToBytes.txt +++ /dev/null @@ -1,6 +0,0 @@ -decryptFileToBytes( srcFile ) -Decrypt a File to another Bytes, -needs to run droid.cipherInit first , and encodingFormat must omitted . -if you want to operate files in Media Storage Device or /sdcard/Android/data/ , run droid.documentTreeShowOpen first . -srcFile (String) -return Decrypted Bytes diff --git a/qsl4a-src/androidhelper/cipher/decryptFileToString.txt b/qsl4a-src/androidhelper/cipher/decryptFileToString.txt deleted file mode 100644 index aaf57a4..0000000 --- a/qsl4a-src/androidhelper/cipher/decryptFileToString.txt +++ /dev/null @@ -1,6 +0,0 @@ -decryptFileToString( srcFile ) -Decrypt a File to another Normal / Base64 String, -needs to run droid.cipherInit first . -if you want to operate files in Media Storage Device or /sdcard/Android/data/ , run droid.documentTreeShowOpen first . -srcFile (String) -return Decrypted String diff --git a/qsl4a-src/androidhelper/cipher/decryptString.txt b/qsl4a-src/androidhelper/cipher/decryptString.txt deleted file mode 100644 index 43f8f79..0000000 --- a/qsl4a-src/androidhelper/cipher/decryptString.txt +++ /dev/null @@ -1,5 +0,0 @@ -decryptString( srcString ) -Decrypt a Base64 String to another Normal / Base64 String, -needs to run droid.cipherInit first . -srcString (String) -return Decrypted String diff --git a/qsl4a-src/androidhelper/cipher/encryptBytes.txt b/qsl4a-src/androidhelper/cipher/encryptBytes.txt deleted file mode 100644 index 42ad1dd..0000000 --- a/qsl4a-src/androidhelper/cipher/encryptBytes.txt +++ /dev/null @@ -1,5 +0,0 @@ -encryptBytes( srcBytes ) -Encrypt a Bytes to another Bytes , -needs to run droid.cipherInit first , and encodingFormat must omitted . -srcBytes (Bytes) -return Encrypted Bytes diff --git a/qsl4a-src/androidhelper/cipher/encryptBytesToFile.txt b/qsl4a-src/androidhelper/cipher/encryptBytesToFile.txt deleted file mode 100644 index 464f745..0000000 --- a/qsl4a-src/androidhelper/cipher/encryptBytesToFile.txt +++ /dev/null @@ -1,7 +0,0 @@ -encryptBytesToFile( srcBytes, dstFile ) -Encrypt a Bytes to another File , -needs to run droid.cipherInit first , and encodingFormat must omitted . -if you want to operate files in Media Storage Device or /sdcard/Android/data/ , run droid.documentTreeShowOpen first . -srcBytes (Bytes) -dstFile (String) -return None diff --git a/qsl4a-src/androidhelper/cipher/encryptFile.txt b/qsl4a-src/androidhelper/cipher/encryptFile.txt deleted file mode 100644 index f712d42..0000000 --- a/qsl4a-src/androidhelper/cipher/encryptFile.txt +++ /dev/null @@ -1,7 +0,0 @@ -encryptFile( srcFile, dstFile ) -Encrypt a File to another File, -needs to run droid.cipherInit first . -if you want to operate files in Media Storage Device or /sdcard/Android/data/ , run droid.documentTreeShowOpen first . -srcFile (String) -dstFile (String) -return None diff --git a/qsl4a-src/androidhelper/cipher/encryptString.txt b/qsl4a-src/androidhelper/cipher/encryptString.txt deleted file mode 100644 index 0f743cd..0000000 --- a/qsl4a-src/androidhelper/cipher/encryptString.txt +++ /dev/null @@ -1,5 +0,0 @@ -encryptString( srcString ) -Encrypt a Normal / Base64 String to another Base64 String , -needs to run droid.cipherInit first . -srcString (String) -return Encrypted Base64 String diff --git a/qsl4a-src/androidhelper/cipher/encryptStringToFile.txt b/qsl4a-src/androidhelper/cipher/encryptStringToFile.txt deleted file mode 100644 index a84a171..0000000 --- a/qsl4a-src/androidhelper/cipher/encryptStringToFile.txt +++ /dev/null @@ -1,7 +0,0 @@ -encryptStringToFile( srcString, dstFile ) -Encrypt a Normal / Base64 String to another File , -needs to run droid.cipherInit first . -if you want to operate files in Media Storage Device or /sdcard/Android/data/ , run droid.documentTreeShowOpen first . -srcString (String) -dstFile (String) -return None diff --git a/qsl4a-src/androidhelper/const/cameraCapturePicture.ini b/qsl4a-src/androidhelper/const/cameraCapturePicture.ini deleted file mode 100644 index 34087e7..0000000 --- a/qsl4a-src/androidhelper/const/cameraCapturePicture.ini +++ /dev/null @@ -1,2 +0,0 @@ -CAMERA_FRONT = 1 -CAMERA_BACK = 0 \ No newline at end of file diff --git a/qsl4a-src/androidhelper/const/fullSetList.ini b/qsl4a-src/androidhelper/const/fullSetList.ini deleted file mode 100644 index aed0134..0000000 --- a/qsl4a-src/androidhelper/const/fullSetList.ini +++ /dev/null @@ -1,5 +0,0 @@ -SIMPLE = 0 -SINGLE_CHOICE_LEFT = 1 -SINGLE_CHOICE_RIGHT = 2 -MULTI_CHOICE_LEFT = 3 -MULTI_CHOICE_RIGHT = 4 \ No newline at end of file diff --git a/qsl4a-src/androidhelper/const/getInstalledPackages.ini b/qsl4a-src/androidhelper/const/getInstalledPackages.ini deleted file mode 100644 index 5add8c6..0000000 --- a/qsl4a-src/androidhelper/const/getInstalledPackages.ini +++ /dev/null @@ -1,5 +0,0 @@ -APPS_ALL = 5 -APPS_USER = 4 -APPS_SYSTEM = 3 -APPS_SYSTEM_UPDATED = 2 -APPS_SYSTEM_NOT_UPDATED = 1 diff --git a/qsl4a-src/androidhelper/const/getTrafficStats.ini b/qsl4a-src/androidhelper/const/getTrafficStats.ini deleted file mode 100644 index e8a1765..0000000 --- a/qsl4a-src/androidhelper/const/getTrafficStats.ini +++ /dev/null @@ -1,3 +0,0 @@ -TOTAL = 1 -MOBILE = 2 -QPYTHON = 4 \ No newline at end of file diff --git a/qsl4a-src/androidhelper/const/makeToast.ini b/qsl4a-src/androidhelper/const/makeToast.ini deleted file mode 100644 index abb81fa..0000000 --- a/qsl4a-src/androidhelper/const/makeToast.ini +++ /dev/null @@ -1,9 +0,0 @@ -SHORT = 0 # 2.0 seconds -LONG = 1 # 3.5 seconds -TEXT_ALIGNMENT_INHERIT = 0 -TEXT_ALIGNMENT_GRAVITY = 1 -TEXT_ALIGNMENT_TEXT_START = 2 -TEXT_ALIGNMENT_TEXT_END = 3 -TEXT_ALIGNMENT_CENTER = 4 -TEXT_ALIGNMENT_VIEW_START = 5 -TEXT_ALIGNMENT_VIEW_END = 6 \ No newline at end of file diff --git a/qsl4a-src/androidhelper/const/recorderCaptureVideo.ini b/qsl4a-src/androidhelper/const/recorderCaptureVideo.ini deleted file mode 100644 index 412ec41..0000000 --- a/qsl4a-src/androidhelper/const/recorderCaptureVideo.ini +++ /dev/null @@ -1,8 +0,0 @@ -QUALITY_8KUHD = 13 -QUALITY_2K = 12 -QUALITY_4KDCI = 10 -QUALITY_2160P = 8 -QUALITY_1080P = 6 -QUALITY_720P = 5 -CAMERA_FRONT = 1 -CAMERA_BACK = 0 \ No newline at end of file diff --git a/qsl4a-src/androidhelper/const/recorderStartScreenRecord.ini b/qsl4a-src/androidhelper/const/recorderStartScreenRecord.ini deleted file mode 100644 index 6e39db2..0000000 --- a/qsl4a-src/androidhelper/const/recorderStartScreenRecord.ini +++ /dev/null @@ -1,12 +0,0 @@ -AUDIO_SOURCE_INVALID = -1 -DEFAULT = 0 -MICROPHONE = 1 -VOICE_UPLINK = 2 # Voice call uplink -VOICE_DOWNLINK = 3 # Voice call downlink -VOICE_CALL = 4 # Voice call uplink + downlink -CAMCORDER = 5 # Microphone audio source tuned for video recording, with the same orientation as the camera if available -VOICE_RECOGNITION = 6 # Microphone audio source tuned for voice recognition -VOICE_COMMUNICATION = 7 # Microphone audio source tuned for voice communications such as VoIP -REMOTE_SUBMIX = 8 # Audio source for a submix of audio streams to be presented remotely -UNPROCESSED = 9 # Microphone audio source tuned for unprocessed (raw) sound if available -VOICE_PERFORMANCE = 10 # Source for capturing audio meant to be processed in real time and played back for live performance \ No newline at end of file diff --git a/qsl4a-src/androidhelper/const/startSensingThreshold.ini b/qsl4a-src/androidhelper/const/startSensingThreshold.ini deleted file mode 100644 index a6976f1..0000000 --- a/qsl4a-src/androidhelper/const/startSensingThreshold.ini +++ /dev/null @@ -1,6 +0,0 @@ -ALL = 1 -ACCELEROMETER = 2 -MAGNETOMETER = 3 -LIGHT = 4 -STEP_COUNTER = 5 -GYROSCOPE = 6 \ No newline at end of file diff --git a/qsl4a-src/androidhelper/const/startSensingTimed.ini b/qsl4a-src/androidhelper/const/startSensingTimed.ini deleted file mode 100644 index a6976f1..0000000 --- a/qsl4a-src/androidhelper/const/startSensingTimed.ini +++ /dev/null @@ -1,6 +0,0 @@ -ALL = 1 -ACCELEROMETER = 2 -MAGNETOMETER = 3 -LIGHT = 4 -STEP_COUNTER = 5 -GYROSCOPE = 6 \ No newline at end of file diff --git a/qsl4a-src/androidhelper/const/takeVideo.ini b/qsl4a-src/androidhelper/const/takeVideo.ini deleted file mode 100644 index c61a39f..0000000 --- a/qsl4a-src/androidhelper/const/takeVideo.ini +++ /dev/null @@ -1,2 +0,0 @@ -QUALITY_HIGH = 1 -QUALITY_LOW = 0 \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/bluetooth/checkBluetoothState.txt b/qsl4a-src/androidhelper/doc/bluetooth/checkBluetoothState.txt deleted file mode 100644 index fe137a5..0000000 --- a/qsl4a-src/androidhelper/doc/bluetooth/checkBluetoothState.txt +++ /dev/null @@ -1,3 +0,0 @@ -checkBluetoothState() -Checks Bluetooth state. -returns: (Boolean) True if Bluetooth is enabled. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/bluetooth/toggleBluetoothState.txt b/qsl4a-src/androidhelper/doc/bluetooth/toggleBluetoothState.txt deleted file mode 100644 index 50a9951..0000000 --- a/qsl4a-src/androidhelper/doc/bluetooth/toggleBluetoothState.txt +++ /dev/null @@ -1,5 +0,0 @@ -toggleBluetoothState(enabled=None,prompt=True) -Toggle Bluetooth on and off. - enabled (Boolean) (optional) - prompt (Boolean) Prompt the user to confirm changing the Bluetooth state. (default=true) -returns: (Boolean) True if Bluetooth is enabled. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/camera/cameraCapturePicture.txt b/qsl4a-src/androidhelper/doc/camera/cameraCapturePicture.txt deleted file mode 100644 index 648ae92..0000000 --- a/qsl4a-src/androidhelper/doc/camera/cameraCapturePicture.txt +++ /dev/null @@ -1,7 +0,0 @@ -cameraCapturePicture(targetPath = None, cameraId = cameraCapturePicture.CAMERA_BACK, useAutoFocus = True) -Take a picture and save it to the specified path. - targetPath (String Optional) if omitted, use default path - cameraId (Integer Optional) - useAutoFocus (Boolean Optional) -Need Camera Permission -returns: targetPath diff --git a/qsl4a-src/androidhelper/doc/camera/cameraSetTorchMode.txt b/qsl4a-src/androidhelper/doc/camera/cameraSetTorchMode.txt deleted file mode 100644 index 2f7245a..0000000 --- a/qsl4a-src/androidhelper/doc/camera/cameraSetTorchMode.txt +++ /dev/null @@ -1,5 +0,0 @@ -cameraSetTorchMode(enabled) -set Camera Torch on/off - enabled (Boolean) True=on, False=off. -Need Camera Permission -return None diff --git a/qsl4a-src/androidhelper/doc/camera/cameraStartPreview.txt b/qsl4a-src/androidhelper/doc/camera/cameraStartPreview.txt deleted file mode 100644 index 17867b4..0000000 --- a/qsl4a-src/androidhelper/doc/camera/cameraStartPreview.txt +++ /dev/null @@ -1,7 +0,0 @@ -cameraStartPreview(resolutionLevel=0,jpegQuality=20,filepath=None) -Start Preview Mode. Throws \'preview\' events. - resolutionLevel (Integer) increasing this number provides higher resolution (default=0) - jpegQuality (Integer) a number from 0-100 (default=20) - filepath (String) Path to store jpeg files. (optional) -Need Camera Permission -returns: (boolean) True if successful diff --git a/qsl4a-src/androidhelper/doc/camera/cameraStopPreview.txt b/qsl4a-src/androidhelper/doc/camera/cameraStopPreview.txt deleted file mode 100644 index 695ebc4..0000000 --- a/qsl4a-src/androidhelper/doc/camera/cameraStopPreview.txt +++ /dev/null @@ -1,2 +0,0 @@ -cameraStopPreview() -Stop the preview mode. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/cipherInit.txt b/qsl4a-src/androidhelper/doc/cipherInit.txt deleted file mode 100644 index dcaa6d3..0000000 --- a/qsl4a-src/androidhelper/doc/cipherInit.txt +++ /dev/null @@ -1,5 +0,0 @@ -cipherInit(key, algorithm="AES/CBC/PKCS5Padding", encodingFormat="", initialVector="") - -Initializes the underlying cipher engine for encryption and decryption operations. This function configures the cryptographic algorithm, key, and initialization vector (IV) mode according to the specified parameters. - -You can read HTML formatted cipherInit() function help by using : droid.viewHtml(f'file://{os.environ["HOME"]}/lib/python3.12/site-packages/androidhelper/cipher/_init.htm') . \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/clipboard/getClipboard.txt b/qsl4a-src/androidhelper/doc/clipboard/getClipboard.txt deleted file mode 100644 index 464a2ef..0000000 --- a/qsl4a-src/androidhelper/doc/clipboard/getClipboard.txt +++ /dev/null @@ -1,3 +0,0 @@ -getClipboard() -Read text from the clipboard. -returns: (String) The text in the clipboard. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/clipboard/setClipboard.txt b/qsl4a-src/androidhelper/doc/clipboard/setClipboard.txt deleted file mode 100644 index 3bfc945..0000000 --- a/qsl4a-src/androidhelper/doc/clipboard/setClipboard.txt +++ /dev/null @@ -1,4 +0,0 @@ -setClipboard(text) -Put text in the clipboard. - text (String) -Creates a new AndroidFacade that simplifies the interface to various Android APIs. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/contacts/contactsGet.txt b/qsl4a-src/androidhelper/doc/contacts/contactsGet.txt deleted file mode 100644 index e38a35f..0000000 --- a/qsl4a-src/androidhelper/doc/contacts/contactsGet.txt +++ /dev/null @@ -1,4 +0,0 @@ -contactsGet(attributes=None) -Returns a List of all contacts. - attributes (JSONArray) (optional) -returns: (List) a List of contacts as Maps \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/contacts/contactsGetAttributes.txt b/qsl4a-src/androidhelper/doc/contacts/contactsGetAttributes.txt deleted file mode 100644 index 13de545..0000000 --- a/qsl4a-src/androidhelper/doc/contacts/contactsGetAttributes.txt +++ /dev/null @@ -1,2 +0,0 @@ -contactsGetAttributes() -Returns a List of all possible attributes for contacts. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/contacts/contactsGetById.txt b/qsl4a-src/androidhelper/doc/contacts/contactsGetById.txt deleted file mode 100644 index 2f9557d..0000000 --- a/qsl4a-src/androidhelper/doc/contacts/contactsGetById.txt +++ /dev/null @@ -1,4 +0,0 @@ -contactsGetById(id,attributes=None) -Returns contacts by ID. - id (Integer) - attributes (JSONArray) (optional) \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/contacts/contactsGetCount.txt b/qsl4a-src/androidhelper/doc/contacts/contactsGetCount.txt deleted file mode 100644 index 5d0952a..0000000 --- a/qsl4a-src/androidhelper/doc/contacts/contactsGetCount.txt +++ /dev/null @@ -1,2 +0,0 @@ -contactsGetCount() -Returns the number of contacts. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/contacts/contactsGetIds.txt b/qsl4a-src/androidhelper/doc/contacts/contactsGetIds.txt deleted file mode 100644 index 46dec39..0000000 --- a/qsl4a-src/androidhelper/doc/contacts/contactsGetIds.txt +++ /dev/null @@ -1,2 +0,0 @@ -contactsGetIds() -Returns a List of all contact IDs. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/contacts/viewContacts.txt b/qsl4a-src/androidhelper/doc/contacts/viewContacts.txt deleted file mode 100644 index e9b1987..0000000 --- a/qsl4a-src/androidhelper/doc/contacts/viewContacts.txt +++ /dev/null @@ -1,3 +0,0 @@ -viewContacts(wait=True) -Opens the list of contacts. -wait (Boolean Optional) wait for the contacts operation end \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/dialog/dialogCreate/dialogCreateAlert.txt b/qsl4a-src/androidhelper/doc/dialog/dialogCreate/dialogCreateAlert.txt deleted file mode 100644 index 410f285..0000000 --- a/qsl4a-src/androidhelper/doc/dialog/dialogCreate/dialogCreateAlert.txt +++ /dev/null @@ -1,24 +0,0 @@ -dialogCreateAlert(title=None,message=None) -Create alert dialog. - title (String) (optional) - message (String) (optional) -Example (python) -import androidhelper -droid=androidhelper.Android() -droid.dialogCreateAlert("I like swords.","Do you like swords?") -droid.dialogSetPositiveButtonText("Yes") -droid.dialogSetNegativeButtonText("No") -droid.dialogShow() -response=droid.dialogGetResponse().result -droid.dialogDismiss() -if response.has_key("which"): - result=response["which"] -if result=="positive": - print("Yay! I like swords too!") -elif result=="negative": - print("Oh. How sad.") -elif response.has_key("canceled"): # Yes, I know it's mispelled. - print("You can't even make up your mind?") -else: - print("Unknown response=",response) - print("Done") \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/dialog/dialogCreate/dialogCreateDatePicker.txt b/qsl4a-src/androidhelper/doc/dialog/dialogCreate/dialogCreateDatePicker.txt deleted file mode 100644 index f5685b3..0000000 --- a/qsl4a-src/androidhelper/doc/dialog/dialogCreate/dialogCreateDatePicker.txt +++ /dev/null @@ -1,5 +0,0 @@ -dialogCreateDatePicker(year=1970,month=1,day=1) -Create date picker dialog. - year (Integer) (default=1970) - month (Integer) (default=1) - day (Integer) (default=1) \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/dialog/dialogCreate/dialogCreateHorizontalProgress.txt b/qsl4a-src/androidhelper/doc/dialog/dialogCreate/dialogCreateHorizontalProgress.txt deleted file mode 100644 index 8c84585..0000000 --- a/qsl4a-src/androidhelper/doc/dialog/dialogCreate/dialogCreateHorizontalProgress.txt +++ /dev/null @@ -1,5 +0,0 @@ -dialogCreateHorizontalProgress(title=None,message=None,maximum_progress=100) -Create a horizontal progress dialog. - title (String) (optional) - message (String) (optional) - maximum progress (Integer) (default=100) \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/dialog/dialogCreate/dialogCreateInput.txt b/qsl4a-src/androidhelper/doc/dialog/dialogCreate/dialogCreateInput.txt deleted file mode 100644 index de0d3c6..0000000 --- a/qsl4a-src/androidhelper/doc/dialog/dialogCreate/dialogCreateInput.txt +++ /dev/null @@ -1,8 +0,0 @@ -dialogCreateInput(title="Value",message="Please enter value:",defaultText=None,inputType=None) -Create a text input dialog. - title (String) title of the input box (default=Value) - message (String) message to display above the input box (default=Please enter value:) - defaultText (String) text to insert into the input box (optional) - inputType (String) type of input data, ie number or text (optional) -For inputType, see InputTypes. Some useful ones are text, number, and textUri. Multiple flags can be -supplied, seperated by "|", ie: "textUri|textAutoComplete" \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/dialog/dialogCreate/dialogCreatePassword.txt b/qsl4a-src/androidhelper/doc/dialog/dialogCreate/dialogCreatePassword.txt deleted file mode 100644 index d3a9762..0000000 --- a/qsl4a-src/androidhelper/doc/dialog/dialogCreate/dialogCreatePassword.txt +++ /dev/null @@ -1,4 +0,0 @@ -dialogCreatePassword(title="Password",message="Please enter password:") -Create a password input dialog. - title (String) title of the input box (default=Password) - message (String) message to display above the input box (default=Please enter password:) \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/dialog/dialogCreate/dialogCreateSeekBar.txt b/qsl4a-src/androidhelper/doc/dialog/dialogCreate/dialogCreateSeekBar.txt deleted file mode 100644 index a0c1ac8..0000000 --- a/qsl4a-src/androidhelper/doc/dialog/dialogCreate/dialogCreateSeekBar.txt +++ /dev/null @@ -1,11 +0,0 @@ -dialogCreateSeekBar(starting_value=50,maximum_value=100,title,message) -Create seek bar dialog. - starting value (Integer) (default=50) - maximum value (Integer) (default=100) - title (String) - message (String) -Will produce "dialog" events on change, containing: -"progress" - Position chosen, between 0 and max -"which" = "seekbar" -"fromuser" = true/false change is from user input -Response will contain a "progress" element. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/dialog/dialogCreate/dialogCreateSpinnerProgress.txt b/qsl4a-src/androidhelper/doc/dialog/dialogCreate/dialogCreateSpinnerProgress.txt deleted file mode 100644 index f52fc59..0000000 --- a/qsl4a-src/androidhelper/doc/dialog/dialogCreate/dialogCreateSpinnerProgress.txt +++ /dev/null @@ -1,5 +0,0 @@ -dialogCreateSpinnerProgress(title=None,message=None,maximum_progress=100) -Create a spinner progress dialog. - title (String) (optional) - message (String) (optional) - maximum progress (Integer) (default=100) \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/dialog/dialogCreate/dialogCreateTimePicker.txt b/qsl4a-src/androidhelper/doc/dialog/dialogCreate/dialogCreateTimePicker.txt deleted file mode 100644 index 0e6642c..0000000 --- a/qsl4a-src/androidhelper/doc/dialog/dialogCreate/dialogCreateTimePicker.txt +++ /dev/null @@ -1,5 +0,0 @@ -dialogCreateTimePicker(hour=0,minute=0,is24hour=False) -Create time picker dialog. - hour (Integer) (default=0) - minute (Integer) (default=0) - is24hour (Boolean) Use 24 hour clock (default=false) \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/dialog/dialogDismiss.txt b/qsl4a-src/androidhelper/doc/dialog/dialogDismiss.txt deleted file mode 100644 index 973f6e9..0000000 --- a/qsl4a-src/androidhelper/doc/dialog/dialogDismiss.txt +++ /dev/null @@ -1,2 +0,0 @@ -dialogDismiss() -Dismiss dialog. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/dialog/dialogGet/dialogGetInput.txt b/qsl4a-src/androidhelper/doc/dialog/dialogGet/dialogGetInput.txt deleted file mode 100644 index 5037d9c..0000000 --- a/qsl4a-src/androidhelper/doc/dialog/dialogGet/dialogGetInput.txt +++ /dev/null @@ -1,29 +0,0 @@ -dialogGetInput ( title = "Value" , message = "Please enter value :" , defaultText = None , messageIsHtml = False ) -Queries the user for a text input. - title (String) title of the input box (default=Value) - message (String) message to display above the input box (default=Please enter value:) - defaultText (String) text to insert into the input box (optional) - messageIsHtml (Boolean) indicates message is Html or Plain Text -The result is the user's input, or None if cancel was hit. - -Example : -from androidhelper import Android -droid = Android() -print(droid.dialogGetInput("Title","Message","Default").result) -print(droid.dialogGetInput("Title","Red Flower","Default",True).result) - -This function is the same as these code : -def dialogGetInput ( title = "Value" , message = "Please enter value :" , defaultText = None , messageIsHtml = False ): - droid.dialogCreateInput(title,message,defaultText,"textMultiLine") - droid.dialogSetMessageIsHtml(messageIsHtml) - droid.dialogSetNegativeButtonText("Cancel") - droid.dialogSetPositiveButtonText("OK") - droid.dialogShow() - try: - result=droid.dialogGetResponse().result - if result["which"]=="positive": - return result['value'] - else: - return None - except: - return None diff --git a/qsl4a-src/androidhelper/doc/dialog/dialogGet/dialogGetPassword.txt b/qsl4a-src/androidhelper/doc/dialog/dialogGet/dialogGetPassword.txt deleted file mode 100644 index 42ad365..0000000 --- a/qsl4a-src/androidhelper/doc/dialog/dialogGet/dialogGetPassword.txt +++ /dev/null @@ -1,4 +0,0 @@ -dialogGetPassword(title="Password",message="Please enter password:") -Queries the user for a password. - title (String) title of the password box (default=Password) - message (String) message to display above the input box (default=Please enter password:) \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/dialog/dialogGet/dialogGetResponse.txt b/qsl4a-src/androidhelper/doc/dialog/dialogGet/dialogGetResponse.txt deleted file mode 100644 index 2c0d134..0000000 --- a/qsl4a-src/androidhelper/doc/dialog/dialogGet/dialogGetResponse.txt +++ /dev/null @@ -1,2 +0,0 @@ -dialogGetResponse() -Returns dialog response. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/dialog/dialogGet/dialogGetSelectedItems.txt b/qsl4a-src/androidhelper/doc/dialog/dialogGet/dialogGetSelectedItems.txt deleted file mode 100644 index 94f5a9e..0000000 --- a/qsl4a-src/androidhelper/doc/dialog/dialogGet/dialogGetSelectedItems.txt +++ /dev/null @@ -1,3 +0,0 @@ -dialogGetSelectedItems() -This method provides list of items user selected. -returns: (Set) Selected items \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/dialog/dialogSet/dialogSetCurrentProgress.txt b/qsl4a-src/androidhelper/doc/dialog/dialogSet/dialogSetCurrentProgress.txt deleted file mode 100644 index e5d4a30..0000000 --- a/qsl4a-src/androidhelper/doc/dialog/dialogSet/dialogSetCurrentProgress.txt +++ /dev/null @@ -1,3 +0,0 @@ -dialogSetCurrentProgress(current) -Set progress dialog current value. - current (Integer) \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/dialog/dialogSet/dialogSetItems.txt b/qsl4a-src/androidhelper/doc/dialog/dialogSet/dialogSetItems.txt deleted file mode 100644 index 9c8e6d8..0000000 --- a/qsl4a-src/androidhelper/doc/dialog/dialogSet/dialogSetItems.txt +++ /dev/null @@ -1,5 +0,0 @@ -dialogSetItems(items) -Set alert dialog list items. - items (JSONArray) -This effectively creates list of options. Clicking on an item will immediately return an "item" -element, which is the index of the selected item. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/dialog/dialogSet/dialogSetMaxProgress.txt b/qsl4a-src/androidhelper/doc/dialog/dialogSet/dialogSetMaxProgress.txt deleted file mode 100644 index 92a7a3b..0000000 --- a/qsl4a-src/androidhelper/doc/dialog/dialogSet/dialogSetMaxProgress.txt +++ /dev/null @@ -1,3 +0,0 @@ -dialogSetMaxProgress(max) -Set progress dialog maximum value. - max (Integer) \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/dialog/dialogSet/dialogSetMessageIsHtml.txt b/qsl4a-src/androidhelper/doc/dialog/dialogSet/dialogSetMessageIsHtml.txt deleted file mode 100644 index b672a1c..0000000 --- a/qsl4a-src/androidhelper/doc/dialog/dialogSet/dialogSetMessageIsHtml.txt +++ /dev/null @@ -1,6 +0,0 @@ -dialogSetMessageIsHtml(messageIsHtml = True) - Make Alert Dialog Message from Plain Text to Html . - messageIsHtml (Boolean Optional) - use this function after dialogCreateAlert/dialogCreateInput/dialogCreatePassword function , - and use this function before dialogSetItems/dialogSetSingleChoiceItems/dialogSetMultiChoiceItems function . - Return None . diff --git a/qsl4a-src/androidhelper/doc/dialog/dialogSet/dialogSetMultiChoiceItems.txt b/qsl4a-src/androidhelper/doc/dialog/dialogSet/dialogSetMultiChoiceItems.txt deleted file mode 100644 index 0d7ed7b..0000000 --- a/qsl4a-src/androidhelper/doc/dialog/dialogSet/dialogSetMultiChoiceItems.txt +++ /dev/null @@ -1,8 +0,0 @@ -dialogSetMultiChoiceItems(items,selected=None) -Set dialog multiple choice items and selection. - items (JSONArray) - selected (JSONArray) list of selected items (optional) -This creates a list of check boxes. You can select multiple items out of the list. A response -will not be returned until the dialog is closed, either with the Cancel key or a button -(positive/negative/neutral). Use dialogGetSelectedItems() to find out what was -selected. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/dialog/dialogSet/dialogSetNegativeButtonText.txt b/qsl4a-src/androidhelper/doc/dialog/dialogSet/dialogSetNegativeButtonText.txt deleted file mode 100644 index a3276d3..0000000 --- a/qsl4a-src/androidhelper/doc/dialog/dialogSet/dialogSetNegativeButtonText.txt +++ /dev/null @@ -1,3 +0,0 @@ -dialogSetNegativeButtonText(text) -Set alert dialog button text. - text (String) \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/dialog/dialogSet/dialogSetNeutralButtonText.txt b/qsl4a-src/androidhelper/doc/dialog/dialogSet/dialogSetNeutralButtonText.txt deleted file mode 100644 index 20cc93b..0000000 --- a/qsl4a-src/androidhelper/doc/dialog/dialogSet/dialogSetNeutralButtonText.txt +++ /dev/null @@ -1,3 +0,0 @@ -dialogSetNeutralButtonText(text) -Set alert dialog button text. - text (String) \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/dialog/dialogSet/dialogSetPositiveButtonText.txt b/qsl4a-src/androidhelper/doc/dialog/dialogSet/dialogSetPositiveButtonText.txt deleted file mode 100644 index 0e45264..0000000 --- a/qsl4a-src/androidhelper/doc/dialog/dialogSet/dialogSetPositiveButtonText.txt +++ /dev/null @@ -1,3 +0,0 @@ -dialogSetPositiveButtonText(text) -Set alert dialog positive button text. - text (String) \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/dialog/dialogSet/dialogSetProgressMessage.txt b/qsl4a-src/androidhelper/doc/dialog/dialogSet/dialogSetProgressMessage.txt deleted file mode 100644 index 5ecf691..0000000 --- a/qsl4a-src/androidhelper/doc/dialog/dialogSet/dialogSetProgressMessage.txt +++ /dev/null @@ -1,3 +0,0 @@ -dialogSetProgressMessage(message) -Set progress dialog message . - message (String) \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/dialog/dialogSet/dialogSetSingleChoiceItems.txt b/qsl4a-src/androidhelper/doc/dialog/dialogSet/dialogSetSingleChoiceItems.txt deleted file mode 100644 index 5f04cd8..0000000 --- a/qsl4a-src/androidhelper/doc/dialog/dialogSet/dialogSetSingleChoiceItems.txt +++ /dev/null @@ -1,7 +0,0 @@ -dialogSetSingleChoiceItems(items,selected=-1) -Set dialog single choice items and selected item. - items (JSONArray) - selected (Integer Optional) selected item index (default=-1 , means no select) -This creates a list of radio buttons. You can select one item out of the list. -A response will not be returned until the dialog is closed, either with the Cancel key or a button -(positive/negative/neutral). Use dialogGetSelectedItems() to find out what was selected. diff --git a/qsl4a-src/androidhelper/doc/dialog/dialogShow/dialogShow.txt b/qsl4a-src/androidhelper/doc/dialog/dialogShow/dialogShow.txt deleted file mode 100644 index d25e2aa..0000000 --- a/qsl4a-src/androidhelper/doc/dialog/dialogShow/dialogShow.txt +++ /dev/null @@ -1,2 +0,0 @@ -dialogShow() -Show dialog. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/dialog/dialogShow/dialogShowAlert.txt b/qsl4a-src/androidhelper/doc/dialog/dialogShow/dialogShowAlert.txt deleted file mode 100644 index 451d3ee..0000000 --- a/qsl4a-src/androidhelper/doc/dialog/dialogShow/dialogShowAlert.txt +++ /dev/null @@ -1,31 +0,0 @@ -dialogShowAlert( title = "Alert", message = "The message of the alert .", positiveButtonText = "OK", negativeButtonText = None, neutralButtonText = None, messageIsHtml = False ) -Show the alert to the user . - title (String Optional) title of the alert box (default=Alert) - message (String Optional) message to display above the alert box (default=The message of the alert .) - positiveButtonText (String Optional) the text to show on the positive button (default=OK) - negativeButtonText (String Optional) the text to show on the negative button (default=None) - neutralButtonText (String Optional) the text to show on the neutral button (default=None) - messageIsHtml (Boolean) indicates message is Html or Plain Text (default=False) - -Example : -from androidhelper import Android -droid = Android() -print(droid.dialogShowAlert("Title","Message","Done").result) -print(droid.dialogShowAlert("Title","Warning Information","Yes","No","Cancel",True).result) - -This function is the same as these code : -dialogShowAlert( title = "Alert", message = "The message of the alert .", positiveButtonText = "OK", negativeButtonText = None, neutralButtonText = None, messageIsHtml = False ): - droid.dialogCreateAlert(title,message) - droid.dialogSetMessageIsHtml(messageIsHtml) - droid.dialogSetPositiveButtonText(positiveButtonText) - droid.dialogSetNegativeButtonText(negativeButtonText) - droid.dialogSetNeutralButtonText(neutralButtonText) - droid.dialogShow() - try: - result=droid.dialogGetResponse().result - if result["which"]=="positive": - return result['value'] - else: - return None - except: - return None \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/dialog/dialogShow/dialogShowMultiChoice.txt b/qsl4a-src/androidhelper/doc/dialog/dialogShow/dialogShowMultiChoice.txt deleted file mode 100644 index eb97c1c..0000000 --- a/qsl4a-src/androidhelper/doc/dialog/dialogShow/dialogShowMultiChoice.txt +++ /dev/null @@ -1,29 +0,0 @@ -dialogShowMultiChoice(self, title = "Alert", message = "The message of the alert .", items = None, selected = None, positiveButtonText = "OK", negativeButtonText = None, neutralButtonText = None, messageIsHtml = False ) -Show the alert to the user . - title (String Optional) title of the alert box (default=Alert) - message (String Optional) message to display above the alert box (default=The message of the alert .) - items (List of String Optional) the list of texts to show on the choice dialog - selected (Integer Optional) the default selected items on the choice dialog - positiveButtonText (String Optional) the text to show on the positive button (default=OK) - negativeButtonText (String Optional) the text to show on the negative button (default=None) - neutralButtonText (String Optional) the text to show on the neutral button (default=None) - messageIsHtml (Boolean) indicates message is Html or Plain Text (default=False) - -Example : -from androidhelper import Android -droid = Android() -print(droid.dialogShowMultiChoice("Title","Message",("Apple","Banana")).result) -print(droid.dialogShowMultiChoice("Title","Warning Information",("First","Second","Third"),[0,2],"Yes","No","Cancel",True).result) - -This function is the same as these code : -dialogShowMultiChoice(self, title = "Alert",message = "The message of the alert .", items = None, selected = None, positiveButtonText = "OK", negativeButtonText = None, neutralButtonText = None, messageIsHtml = False ): - droid.dialogCreateAlert(title,message) - droid.dialogSetMessageIsHtml(messageIsHtml) - droid.dialogSetPositiveButtonText(positiveButtonText) - droid.dialogSetNegativeButtonText(negativeButtonText) - droid.dialogSetNeutralButtonText(neutralButtonText) - droid.dialogSetMultiChoiceItems(items,selected) - droid.dialogShow() - result = droid.dialogGetResponse().result - result["selected"] = droid.dialogGetSelected().result - return result \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/dialog/dialogShow/dialogShowSimpleChoice.txt b/qsl4a-src/androidhelper/doc/dialog/dialogShow/dialogShowSimpleChoice.txt deleted file mode 100644 index fe36a11..0000000 --- a/qsl4a-src/androidhelper/doc/dialog/dialogShow/dialogShowSimpleChoice.txt +++ /dev/null @@ -1,26 +0,0 @@ -dialogShowSimpleChoice(self, title = "Alert", message = "The message of the alert .", items = None, positiveButtonText = "OK", negativeButtonText = None, neutralButtonText = None, messageIsHtml = False ) -Show the alert to the user . - title (String Optional) title of the alert box (default=Alert) - message (String Optional) message to display above the alert box (default=The message of the alert .) - items (List of String Optional) the list of texts to show on the choice dialog - positiveButtonText (String Optional) the text to show on the positive button (default=OK) - negativeButtonText (String Optional) the text to show on the negative button (default=None) - neutralButtonText (String Optional) the text to show on the neutral button (default=None) - messageIsHtml (Boolean) indicates message is Html or Plain Text (default=False) - -Example : -from androidhelper import Android -droid = Android() -print(droid.dialogShowSimpleChoice("Title","Message",("Apple","Banana")).result) -print(droid.dialogShowSimpleChoice("Title","Warning Information",("First","Second","Third"),"Yes","No","Cancel",True).result) - -This function is the same as these code : -dialogShowSimpleChoice(self, title = "Alert", message = "The message of the alert .", items = None, messageIsHtml = False ): - droid.dialogCreateAlert(title,message) - droid.dialogSetMessageIsHtml(messageIsHtml) - droid.dialogSetPositiveButtonText(positiveButtonText) - droid.dialogSetNegativeButtonText(negativeButtonText) - droid.dialogSetNeutralButtonText(neutralButtonText) - droid.dialogSetItems(items) - droid.dialogShow() - return droid.dialogGetResponse().result \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/dialog/dialogShow/dialogShowSingleChoice.txt b/qsl4a-src/androidhelper/doc/dialog/dialogShow/dialogShowSingleChoice.txt deleted file mode 100644 index 3115306..0000000 --- a/qsl4a-src/androidhelper/doc/dialog/dialogShow/dialogShowSingleChoice.txt +++ /dev/null @@ -1,29 +0,0 @@ -dialogShowSingleChoice(self, title = "Alert",message = "The message of the alert .", items = None, selected = -1, positiveButtonText = "OK", negativeButtonText = None, neutralButtonText = None, messageIsHtml = False ) -Show the alert to the user . - title (String Optional) title of the alert box (default=Alert) - message (String Optional) message to display above the alert box (default=The message of the alert .) - items (List of String Optional) the list of texts to show on the choice dialog - selected (Integer Optional) the default selected item on the choice dialog - positiveButtonText (String Optional) the text to show on the positive button (default=OK) - negativeButtonText (String Optional) the text to show on the negative button (default=None) - neutralButtonText (String Optional) the text to show on the neutral button (default=None) - messageIsHtml (Boolean) indicates message is Html or Plain Text (default=False) - -Example : -from androidhelper import Android -droid = Android() -print(droid.dialogShowSingleChoice("Title","Message",("Apple","Banana")).result) -print(droid.dialogShowSingleChoice("Title","Warning Information",("First","Second","Third"),1,"Yes","No","Cancel",True).result) - -This function is the same as these code : -dialogShowSingleChoice(self, title = "Alert",message = "The message of the alert .", items = None, selected = -1, positiveButtonText = "OK", negativeButtonText = None, neutralButtonText = None, messageIsHtml = False ): - droid.dialogCreateAlert(title,message) - droid.dialogSetMessageIsHtml(messageIsHtml) - droid.dialogSetPositiveButtonText(positiveButtonText) - droid.dialogSetNegativeButtonText(negativeButtonText) - droid.dialogSetNeutralButtonText(neutralButtonText) - droid.dialogSetSingleChoiceItems(items,selected) - droid.dialogShow() - result = droid.dialogGetResponse().result - result["selected"] = droid.dialogGetSelected().result[0] - return result \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/event/eventClearBuffer.txt b/qsl4a-src/androidhelper/doc/event/eventClearBuffer.txt deleted file mode 100644 index 18b7dfa..0000000 --- a/qsl4a-src/androidhelper/doc/event/eventClearBuffer.txt +++ /dev/null @@ -1,3 +0,0 @@ -eventClearBuffer() -Clears all events from the event buffer. -Example (python): droid.eventClearBuffer() \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/event/eventGetBrodcastCategories.txt b/qsl4a-src/androidhelper/doc/event/eventGetBrodcastCategories.txt deleted file mode 100644 index 79a91c4..0000000 --- a/qsl4a-src/androidhelper/doc/event/eventGetBrodcastCategories.txt +++ /dev/null @@ -1,2 +0,0 @@ -eventGetBrodcastCategories() -Lists all the broadcast signals we are listening for \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/event/eventPoll.txt b/qsl4a-src/androidhelper/doc/event/eventPoll.txt deleted file mode 100644 index e560113..0000000 --- a/qsl4a-src/androidhelper/doc/event/eventPoll.txt +++ /dev/null @@ -1,22 +0,0 @@ -eventPoll(number_of_events=1) -Returns and removes the oldest n events (i.e. location or sensor update, etc.) from the event buffer. - number_of_events (Integer) (default=1) -returns: (List) A List of Maps of event properties. -Actual data returned in the map will depend on the type of event. -Example (python): -import android, time -droid = android.Android() -droid.startSensing() -time.sleep(1) -droid.eventClearBuffer() -time.sleep(1) -e = eventPoll(1).result -event_entry_number = 0 -x = e[event_entry_ number]['data']['xforce'] -e has the format: -[{u'data': {u'accuracy': 0, u'pitch': -0.48766891956329345, u'xmag': -5.6875, u'azimuth': -0.3312483489513397, u'zforce': 8.3492730000000002, u'yforce': 4.5628165999999997, u'time': -1297072704.813, u'ymag': -11.125, u'zmag': -42.375, u'roll': -0.059393649548292161, u'xforce': -0.42223078000000003}, u'name': u'sensors', u'time': 1297072704813000L}] -x has the string value of the x force data (0.42223078000000003) at the time of the event -entry. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/event/eventPost.txt b/qsl4a-src/androidhelper/doc/event/eventPost.txt deleted file mode 100644 index 50ddc4b..0000000 --- a/qsl4a-src/androidhelper/doc/event/eventPost.txt +++ /dev/null @@ -1,11 +0,0 @@ -eventPost(name,data,enqueue=None) -Post an event to the event queue. - name (String) Name of event - data (String) Data contained in event. - enqueue (Boolean) Set to False if you don\'t want your events to be added to the event queue, just dispatched. (optional) (default=false) -Example: -import android -from datetime import datetime -droid = android.Android() -t = datetime.now() -droid.eventPost('Some Event', t) \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/event/eventRegisterForBroadcast.txt b/qsl4a-src/androidhelper/doc/event/eventRegisterForBroadcast.txt deleted file mode 100644 index 2477da1..0000000 --- a/qsl4a-src/androidhelper/doc/event/eventRegisterForBroadcast.txt +++ /dev/null @@ -1,5 +0,0 @@ -eventRegisterForBroadcast(category,enqueue=True) -Registers a listener for a new broadcast signal - category (String) - enqueue (Boolean) Should this events be added to the event queue or only dispatched (default=true) -Registers a listener for a new broadcast signal \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/event/eventUnregisterForBroadcast.txt b/qsl4a-src/androidhelper/doc/event/eventUnregisterForBroadcast.txt deleted file mode 100644 index 5dd523d..0000000 --- a/qsl4a-src/androidhelper/doc/event/eventUnregisterForBroadcast.txt +++ /dev/null @@ -1,3 +0,0 @@ -eventUnregisterForBroadcast(category) -Stop listening for a broadcast signal - category (String) \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/event/eventWait.txt b/qsl4a-src/androidhelper/doc/event/eventWait.txt deleted file mode 100644 index 3f43811..0000000 --- a/qsl4a-src/androidhelper/doc/event/eventWait.txt +++ /dev/null @@ -1,4 +0,0 @@ -eventWait(timeout=None) -Blocks until an event occurs. The returned event is removed from the buffer. - timeout (Integer) the maximum time to wait (optional) -returns: (Event) Map of event properties. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/event/eventWaitFor.txt b/qsl4a-src/androidhelper/doc/event/eventWaitFor.txt deleted file mode 100644 index 32b71e2..0000000 --- a/qsl4a-src/androidhelper/doc/event/eventWaitFor.txt +++ /dev/null @@ -1,5 +0,0 @@ -eventWaitFor(eventName,timeout=None) -Blocks until an event with the supplied name occurs. The returned event is not removed from the buffer. - eventName (String) - timeout (Integer) the maximum time to wait (in ms) (optional) -returns: (Event) Map of event properties. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/event/receiveEvent.txt b/qsl4a-src/androidhelper/doc/event/receiveEvent.txt deleted file mode 100644 index 7136183..0000000 --- a/qsl4a-src/androidhelper/doc/event/receiveEvent.txt +++ /dev/null @@ -1,4 +0,0 @@ -receiveEvent() -Returns and removes the oldest event (i.e. location or sensor update, etc.) from the event buffer. -returns: (Event) Map of event properties. -Deprecated in r4. Use eventPoll instead. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/event/rpcPostEvent.txt b/qsl4a-src/androidhelper/doc/event/rpcPostEvent.txt deleted file mode 100644 index a907e71..0000000 --- a/qsl4a-src/androidhelper/doc/event/rpcPostEvent.txt +++ /dev/null @@ -1,5 +0,0 @@ -rpcPostEvent(name,data) -Post an event to the event queue. - name (String) - data (String) -Deprecated in r4. Use eventPost instead. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/event/startEventDispatcher.txt b/qsl4a-src/androidhelper/doc/event/startEventDispatcher.txt deleted file mode 100644 index 27c5fc6..0000000 --- a/qsl4a-src/androidhelper/doc/event/startEventDispatcher.txt +++ /dev/null @@ -1,3 +0,0 @@ -startEventDispatcher(port=None) -Opens up a socket where you can read for events posted - port (Integer) Port to use (optional) (default=0) \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/event/stopEventDispatcher.txt b/qsl4a-src/androidhelper/doc/event/stopEventDispatcher.txt deleted file mode 100644 index 51cb217..0000000 --- a/qsl4a-src/androidhelper/doc/event/stopEventDispatcher.txt +++ /dev/null @@ -1,2 +0,0 @@ -stopEventDispatcher() -Stops the event server, you can\'t read in the port anymore \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/event/waitForEvent.txt b/qsl4a-src/androidhelper/doc/event/waitForEvent.txt deleted file mode 100644 index 1f6f02d..0000000 --- a/qsl4a-src/androidhelper/doc/event/waitForEvent.txt +++ /dev/null @@ -1,6 +0,0 @@ -waitForEvent(eventName,timeout=None) -Blocks until an event with the supplied name occurs. The returned event is not removed from the buffer. - eventName (String) - timeout (Integer) the maximum time to wait (optional) -returns: (Event) Map of event properties. -Deprecated in r4. Use eventWaitFor instead. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/file/Uri.txt b/qsl4a-src/androidhelper/doc/file/Uri.txt deleted file mode 100644 index 091ba1d..0000000 --- a/qsl4a-src/androidhelper/doc/file/Uri.txt +++ /dev/null @@ -1,3 +0,0 @@ -Uri(uriString) - Convert Uri String to Uri Object, use for droid.makeIntent / droid.startActivity / droid.startActivityForResult / droid.startActivityIntent extras . - return Uri Object . \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/file/documentTreeShowOpen.txt b/qsl4a-src/androidhelper/doc/file/documentTreeShowOpen.txt deleted file mode 100644 index 0c08a28..0000000 --- a/qsl4a-src/androidhelper/doc/file/documentTreeShowOpen.txt +++ /dev/null @@ -1,16 +0,0 @@ -documentTreeShowOpen( rootPath = '/sdcard' ) - Request for Media Storage Write or "/sdcard/Android/" Read/Write Permissions . - documentFileShowOpen is the same as documentTreeShowOpen . - rootPath (String) - Return Uri String if Success , if Fail return None . - -Use This Function to Read/Write Media Storage Device or "/sdcard/Android/" , - documentTreeShowOpen will get all documentFile functions to androidhelper at the same time , - documentFile functions are very slow . - - For Media Storage Devices : - use rootPath = "/storage/ABCD-EFGH" or "/mnt/ext_sdcard" , return $URI . - - For "/sdcard/Android/" : - For Android 7-10 , use rootPath = "/sdcard/Android/data" , return "file://" ; - For Android 11-13 , use rootPath = "/sdcard/Android/data/the.package.name" , return $URI . diff --git a/qsl4a-src/androidhelper/doc/file/getSdCardPaths.txt b/qsl4a-src/androidhelper/doc/file/getSdCardPaths.txt deleted file mode 100644 index 42caf76..0000000 --- a/qsl4a-src/androidhelper/doc/file/getSdCardPaths.txt +++ /dev/null @@ -1,2 +0,0 @@ -getSdCardPaths() - Return Paths of Emulated, SD Card and USB Disk . diff --git a/qsl4a-src/androidhelper/doc/file/openFile.txt b/qsl4a-src/androidhelper/doc/file/openFile.txt deleted file mode 100644 index f5f1c7f..0000000 --- a/qsl4a-src/androidhelper/doc/file/openFile.txt +++ /dev/null @@ -1,5 +0,0 @@ -openFile(path, type=None, wait=True) -Open the file in the default open mode with content:// . - path (String) file path - type (String Optional) MIME-type self-defined - wait (Boolean Optional) wait for the end of the file open operation \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/file/pathToUri.txt b/qsl4a-src/androidhelper/doc/file/pathToUri.txt deleted file mode 100644 index 93aae1c..0000000 --- a/qsl4a-src/androidhelper/doc/file/pathToUri.txt +++ /dev/null @@ -1,9 +0,0 @@ -pathToUri(path, fileProvider=True, uriObject=False) -Convert normal path to content:// . - path (String) file path - fileProvider (Boolean Optional) - True is default means content://.provider/ , - False is default means content://media/external/ . - uriObject (Boolean Optional) - True means return uriObject , - False means return uriString . \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/ftp/ftpGet.txt b/qsl4a-src/androidhelper/doc/ftp/ftpGet.txt deleted file mode 100644 index 0ddfc27..0000000 --- a/qsl4a-src/androidhelper/doc/ftp/ftpGet.txt +++ /dev/null @@ -1,3 +0,0 @@ -ftpGet() - ftp get ip address and port number . - return a list of ftp address like ["ftp://12.34.56.78:4321", "ftp://12.34.56.90:4321"], if ftp address is "ftp://0.0.0.0:4321", means ftp address not valid, you should connect wifi or open wifi hotspot . diff --git a/qsl4a-src/androidhelper/doc/ftp/ftpIsRunning.txt b/qsl4a-src/androidhelper/doc/ftp/ftpIsRunning.txt deleted file mode 100644 index 1f70e90..0000000 --- a/qsl4a-src/androidhelper/doc/ftp/ftpIsRunning.txt +++ /dev/null @@ -1,2 +0,0 @@ -ftpIsRunning() - return True/False \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/ftp/ftpSet.txt b/qsl4a-src/androidhelper/doc/ftp/ftpSet.txt deleted file mode 100644 index 041272b..0000000 --- a/qsl4a-src/androidhelper/doc/ftp/ftpSet.txt +++ /dev/null @@ -1,9 +0,0 @@ -ftpSet(port = None, rootDir = None, username = None, password = None) - Change FTP Server Settings - port (Integer Optional) - chrootDir (String Optional) - username (String Optional) - password (String Optional) - return a Dict of port, rootDir, username and password , - you can use this function with no parameter to get FTP Server Settings , - if the current FTP Server is running, you need to restart the FTP Server to take effect . \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/ftp/ftpStart.txt b/qsl4a-src/androidhelper/doc/ftp/ftpStart.txt deleted file mode 100644 index 8e67872..0000000 --- a/qsl4a-src/androidhelper/doc/ftp/ftpStart.txt +++ /dev/null @@ -1,3 +0,0 @@ -ftpStart() - Start ftp server . - return a list of ftp address like ["ftp://12.34.56.78:4321", "ftp://12.34.56.90:4321"], if ftp address is "ftp://0.0.0.0:4321", means ftp address not valid, you should connect wifi or open wifi hotspot . diff --git a/qsl4a-src/androidhelper/doc/ftp/ftpStatus.txt b/qsl4a-src/androidhelper/doc/ftp/ftpStatus.txt deleted file mode 100644 index 306472b..0000000 --- a/qsl4a-src/androidhelper/doc/ftp/ftpStatus.txt +++ /dev/null @@ -1,2 +0,0 @@ -ftpStatus() - Return the Status of the FTP Server , include "running", "stopped", "uninitial" and "failed" . \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/ftp/ftpStop.txt b/qsl4a-src/androidhelper/doc/ftp/ftpStop.txt deleted file mode 100644 index f7471b9..0000000 --- a/qsl4a-src/androidhelper/doc/ftp/ftpStop.txt +++ /dev/null @@ -1,3 +0,0 @@ -ftpStop() - Stop ftp server . - return None \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/full/fullDismiss.txt b/qsl4a-src/androidhelper/doc/full/fullDismiss.txt deleted file mode 100644 index 4a4496d..0000000 --- a/qsl4a-src/androidhelper/doc/full/fullDismiss.txt +++ /dev/null @@ -1,2 +0,0 @@ -fullDismiss() -Dismiss Full Screen. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/full/fullGetListSelected.txt b/qsl4a-src/androidhelper/doc/full/fullGetListSelected.txt deleted file mode 100644 index 54247e4..0000000 --- a/qsl4a-src/androidhelper/doc/full/fullGetListSelected.txt +++ /dev/null @@ -1,8 +0,0 @@ -fullGetListSelected(id) -Get option(s) for the displayed text/html list , -Get one option for the single choice list , -Get multiple options for the multiple choice list . - id (String) id of layout widget -return an Integer or a List of Integers . - Integer : return an integer greater than or equal to zero when selected from a Single Choice list , -1 means all unselect for Single Choice list ; - List of Integers : return a list of integer greater than or equal to zero like [0, 4, 7] when selected from a Multi Choice list , 0 is the first item , the last item is ( length - 1 ) , [ ] ( empty list ) means all unselected for Multi Choice list , [ 0, 1, 2, …… , length-1 ] means all selected for Multi Choice list . \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/full/fullGetProperties.txt b/qsl4a-src/androidhelper/doc/full/fullGetProperties.txt deleted file mode 100644 index 34fb7ad..0000000 --- a/qsl4a-src/androidhelper/doc/full/fullGetProperties.txt +++ /dev/null @@ -1,4 +0,0 @@ -fullGetProperties(ids,property) -Get a fullscreen property for many specific widgets - ids (String List) ids of layout widgets - property (String) property of layout widgets \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/full/fullGetProperty.txt b/qsl4a-src/androidhelper/doc/full/fullGetProperty.txt deleted file mode 100644 index 89c6b27..0000000 --- a/qsl4a-src/androidhelper/doc/full/fullGetProperty.txt +++ /dev/null @@ -1,4 +0,0 @@ -fullGetProperty(id,property) -Get a fullscreen property for a specific widget - id (String) id of layout widget - property (String) property of layout widget \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/full/fullGetScreenShot.txt b/qsl4a-src/androidhelper/doc/full/fullGetScreenShot.txt deleted file mode 100644 index d5a6063..0000000 --- a/qsl4a-src/androidhelper/doc/full/fullGetScreenShot.txt +++ /dev/null @@ -1,4 +0,0 @@ -fullGetScreenShot(path = None) -Get the Full Screen Activity ScreenShot to path . - path (String Optional) jpeg file path, if omitted, use default path -return path \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/full/fullQuery.txt b/qsl4a-src/androidhelper/doc/full/fullQuery.txt deleted file mode 100644 index 4f25815..0000000 --- a/qsl4a-src/androidhelper/doc/full/fullQuery.txt +++ /dev/null @@ -1,2 +0,0 @@ -fullQuery() -Get Fullscreen Properties \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/full/fullQueryDetail.txt b/qsl4a-src/androidhelper/doc/full/fullQueryDetail.txt deleted file mode 100644 index f53c9d3..0000000 --- a/qsl4a-src/androidhelper/doc/full/fullQueryDetail.txt +++ /dev/null @@ -1,3 +0,0 @@ -fullQueryDetail(id) -Get fullscreen properties for a specific widget - id (String) id of layout widget \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/full/fullSetList.txt b/qsl4a-src/androidhelper/doc/full/fullSetList.txt deleted file mode 100644 index 7d2e26d..0000000 --- a/qsl4a-src/androidhelper/doc/full/fullSetList.txt +++ /dev/null @@ -1,29 +0,0 @@ -fullSetList(id, list, isHtml=False, listType=fullSetList.SIMPLE) - Attach a text/html list to a fullscreen widget. - - • id (String) : - The ID of the layout widget to attach the list. - - • list (Message List of String or Dictionary) : - * The list of items to display. - * String: A plain text string. - * Dictionary: An object with the following documented keys: - - msg (String Optional) : The content to display. If omitted, displays a blank line. - - bg (String Optional) : Background color (Hex format, e.g., "#RRGGBB", "#AARRGGBB"). - - fg (String Optional) : Foreground color (Text color, Hex format, e.g., "#RRGGBB", "#AARRGGBB"). - - • isHtml (Boolean, Optional, Default=False) : - If True , treats the message (List of String or List of Dictionary with the msg field) as HTML. If False , treats it as plain text. - - • listType (Integer, Optional, Default=0) : - - The style of the list. - - 0 : Simple List (No selector) - - 1 : Single Choice List (Radio button on left) - - 2 : Single Choice List (Radio button on right) - - 3 : Multi Choice List (Checkbox on left) - - 4 : Multi Choice List (Checkbox on right) - - Examples : - fullSetList('list1', ('item1','item2','item3'), False, fullSetList.SIMPLE) - fullSetList('list2', ({'msg':'item1','bg':'#00ff00','fg':'#ff00ff'},{'msg':'item2','bg':'#ff0000','fg':'#00ffff'},{'msg':'item3','bg':'#0000ff','fg':'#ffff00'}), False, fullSetList.SINGLE_CHOICE_LEFT) - fullSetList('list3', ('item1','item2','item3'), True, fullSetList.MULTI_CHOICE_RIGHT) diff --git a/qsl4a-src/androidhelper/doc/full/fullSetList2.txt b/qsl4a-src/androidhelper/doc/full/fullSetList2.txt deleted file mode 100644 index 6057b77..0000000 --- a/qsl4a-src/androidhelper/doc/full/fullSetList2.txt +++ /dev/null @@ -1,15 +0,0 @@ -fullSetList2(id,list,intRes) -Attach a 2-line list to a fullscreen widget - id (String) id of layout widget - list (JSONArray) List to set like [1Up,1Down,2Up,2Down,……] - intRes (JSONArray) constant integer like : - [R.layout.simple_list_item_2,R.id.text1,R.id.text2] - [R.layout.simple_list_item_1,R.id.text1] - [R.layout.simple_spinner_item,R.id.text1,R.layout.simple_spinner_dropdown_item] - [R.layout.simple_expandable_list_item_2,R.id.text1,R.id.text2] - [R.layout.simple_expandable_list_item_1,R.id.text1] - [R.layout.simple_spinner_dropdown_item,R.id.text1,R.layout.simple_spinner_item] - R is ( - from androidhelper.R import R - R=R() - ) \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/full/fullSetListSelected.txt b/qsl4a-src/androidhelper/doc/full/fullSetListSelected.txt deleted file mode 100644 index 8c38b84..0000000 --- a/qsl4a-src/androidhelper/doc/full/fullSetListSelected.txt +++ /dev/null @@ -1,10 +0,0 @@ -fullSetListSelected(id,selected) -Set option(s) for the displayed text/html list , -Set one option for the single choice list , -Set multiple options for the multiple choice list . - id (String) id of layout widget - selected (Integer or List of Integers or Boolean) - Integer for Single Choice : an Integer greater than or equal to zero for Single Choice , -1 means all unselect ; - List of Integers for Multi Choice : a List of Integers greater than or equal to zero like [0, 3, 5] , 0 is the first item , the last item is ( length - 1 ) , [ ] ( empty list ) means all unselect , [ 0, 1, 2, …… , length-1 ] means all select ; - Boolean for Multi Choice : all True or all False . -return None \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/full/fullSetProperties.txt b/qsl4a-src/androidhelper/doc/full/fullSetProperties.txt deleted file mode 100644 index 5a246c4..0000000 --- a/qsl4a-src/androidhelper/doc/full/fullSetProperties.txt +++ /dev/null @@ -1,5 +0,0 @@ -fullSetProperties(ids,property,value) -Set many fullscreen widgets' a property - ids (String List) ids of layout widgets - property (String) name of property to set - value (String) value to set property to \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/full/fullSetProperty.txt b/qsl4a-src/androidhelper/doc/full/fullSetProperty.txt deleted file mode 100644 index 20b2cf8..0000000 --- a/qsl4a-src/androidhelper/doc/full/fullSetProperty.txt +++ /dev/null @@ -1,5 +0,0 @@ -fullSetProperty(id,property,value) -Set a fullscreen widget's a property - id (String) id of layout widget - property (String) name of property to set - value (String) value to set property to \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/full/fullShow.txt b/qsl4a-src/androidhelper/doc/full/fullShow.txt deleted file mode 100644 index 6e0d184..0000000 --- a/qsl4a-src/androidhelper/doc/full/fullShow.txt +++ /dev/null @@ -1,9 +0,0 @@ -fullShow(layout,title=None,theme=None) -Show Full Screen. - layout (String) String containing View layout - title (String Optional) - theme (Integer Optional) See https://developer.android.google.cn/reference/android/R.style for Integer constant . -Commonly used theme please use : - from androidhelper.R import R - R=R() - R.style \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/html/htmlPictureGetSize.txt b/qsl4a-src/androidhelper/doc/html/htmlPictureGetSize.txt deleted file mode 100644 index f0250a0..0000000 --- a/qsl4a-src/androidhelper/doc/html/htmlPictureGetSize.txt +++ /dev/null @@ -1,2 +0,0 @@ -htmlPictureGetSize() - Return a dict of widthFixed , heightFixed , widthRatio , heightRatio . diff --git a/qsl4a-src/androidhelper/doc/html/htmlPictureSetSize.txt b/qsl4a-src/androidhelper/doc/html/htmlPictureSetSize.txt deleted file mode 100644 index f67366a..0000000 --- a/qsl4a-src/androidhelper/doc/html/htmlPictureSetSize.txt +++ /dev/null @@ -1,10 +0,0 @@ -htmlPictureSetSize(widthFixed = None, heightFixed = None, widthRatio = None, heightRatio = None) - widthFixed , heightFixed (Integer Optional) : - set the picture fixed size, - if ommitted to None, use intrinsic width and height . - widthRatio , heightRatio (Float Optional) : - set the picture size ratio, - if >1.0 , pictures will zoom in , - if <1.0 , pictures will zoom out, - if ommitted to None or is 1.0 , the picture width and height will not zoom . - Return None . diff --git a/qsl4a-src/androidhelper/doc/html/viewHtml.txt b/qsl4a-src/androidhelper/doc/html/viewHtml.txt deleted file mode 100644 index 308330c..0000000 --- a/qsl4a-src/androidhelper/doc/html/viewHtml.txt +++ /dev/null @@ -1,6 +0,0 @@ -viewHtml(path,title=None,wait=True) -webViewShow is the same as viewHtml -Opens the browser to display a local HTML/text/audio/video File or http(s) Website . - path (String) the path to the HTML/text/audio/video File or http(s) Website - title (String Optional) title bar text, if omitted, use app name as default title, if title is empty String, title bar will hidden to show a full screen webpage. - wait (Boolean Optional) wait for the file operation end \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/intent/getIntent.txt b/qsl4a-src/androidhelper/doc/intent/getIntent.txt deleted file mode 100644 index 68b00b7..0000000 --- a/qsl4a-src/androidhelper/doc/intent/getIntent.txt +++ /dev/null @@ -1,2 +0,0 @@ -getIntent() -Returns the intent that launched the script. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/intent/launch.txt b/qsl4a-src/androidhelper/doc/intent/launch.txt deleted file mode 100644 index 4687dea..0000000 --- a/qsl4a-src/androidhelper/doc/intent/launch.txt +++ /dev/null @@ -1,8 +0,0 @@ -launch(classname=None,packagename=None,wait=True) -Start activity with the given classname and/or packagename . -If classname is packagename.sub0, packagename can be omitted, -if classname is packagename.sub1.sub2.…, packagename must be specified, -if classname is omitted, packagename must be specified . - classname (String Optional) - packagename (String Optional) - wait (Boolean Optional) wait for the launched application operation end \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/intent/makeIntent.txt b/qsl4a-src/androidhelper/doc/intent/makeIntent.txt deleted file mode 100644 index fb4c469..0000000 --- a/qsl4a-src/androidhelper/doc/intent/makeIntent.txt +++ /dev/null @@ -1,12 +0,0 @@ -makeIntent(action,uri=None,type=None,extras=None,categories=None,packagename=None,classname=None,flags=None) -Create an Intent. - action (String) - uri (String) (optional) - type (String) MIME type/subtype of the URI (optional) - extras (JSONObject) a Map of extras to add to the Intent (optional) : - elements of extras support Integer, Float, String, List, Dict, single Bytes, Uri(String) . - categories (JSONArray) a List of categories to add to the Intent (optional) - packagename (String) name of package. If used, requires classname to be useful (optional) - classname (String) name of class. If used, requires packagename to be useful (optional) - flags (Integer) Intent flags (optional) -returns: (Intent) An object representing an Intent \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/intent/pick.txt b/qsl4a-src/androidhelper/doc/intent/pick.txt deleted file mode 100644 index c1b1ed8..0000000 --- a/qsl4a-src/androidhelper/doc/intent/pick.txt +++ /dev/null @@ -1,4 +0,0 @@ -pick(uri) -Display content to be picked by URI (e.g. contacts) - uri (String) -returns: (Intent) A map of result values. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/intent/pickContact.txt b/qsl4a-src/androidhelper/doc/intent/pickContact.txt deleted file mode 100644 index 3046b59..0000000 --- a/qsl4a-src/androidhelper/doc/intent/pickContact.txt +++ /dev/null @@ -1,3 +0,0 @@ -pickContact() -Displays a list of contacts to pick from. -returns: (Intent) A map of result values. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/intent/search.txt b/qsl4a-src/androidhelper/doc/intent/search.txt deleted file mode 100644 index 2be1761..0000000 --- a/qsl4a-src/androidhelper/doc/intent/search.txt +++ /dev/null @@ -1,3 +0,0 @@ -search(query) -Starts a search for the given query. - query (String) \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/intent/send.txt b/qsl4a-src/androidhelper/doc/intent/send.txt deleted file mode 100644 index f42ceda..0000000 --- a/qsl4a-src/androidhelper/doc/intent/send.txt +++ /dev/null @@ -1,6 +0,0 @@ -send(uri,type=None,extras=None,wait=True) -Start activity with send action by URI (i.e. browser, contacts, etc.). - uri (String) - type (String) MIME type/subtype of the URI (optional) - extras (JSONObject) a Map of extras to add to the Intent (optional) - wait (Boolean Optional) wait for the send operation end \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/intent/sendBroadcast.txt b/qsl4a-src/androidhelper/doc/intent/sendBroadcast.txt deleted file mode 100644 index 6bac174..0000000 --- a/qsl4a-src/androidhelper/doc/intent/sendBroadcast.txt +++ /dev/null @@ -1,9 +0,0 @@ -sendBroadcast(action,uri=None,type=None,extras=None,packagename=None,classname=None,flags=None) -Send a broadcast. - action (String) - uri (String) (optional) - type (String) MIME type/subtype of the URI (optional) - extras (JSONObject) a Map of extras to add to the Intent (optional) - packagename (String) name of package. If used, requires classname to be useful (optional) - classname (String) name of class. If used, requires packagename to be useful (optional) - flags (Integer) Intent flags (optional) diff --git a/qsl4a-src/androidhelper/doc/intent/sendBroadcastIntent.txt b/qsl4a-src/androidhelper/doc/intent/sendBroadcastIntent.txt deleted file mode 100644 index 79590c4..0000000 --- a/qsl4a-src/androidhelper/doc/intent/sendBroadcastIntent.txt +++ /dev/null @@ -1,3 +0,0 @@ -sendBroadcastIntent(intent) -Send Broadcast Intent - intent (Intent) Intent in the format as returned from makeIntent \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/intent/sendEmail.txt b/qsl4a-src/androidhelper/doc/intent/sendEmail.txt deleted file mode 100644 index 5b80857..0000000 --- a/qsl4a-src/androidhelper/doc/intent/sendEmail.txt +++ /dev/null @@ -1,8 +0,0 @@ -sendEmail(to,subject,text,attachmentPath=None,extras=None,wait=True) -Launches an activity that sends an e-mail message to given recipient(s) or attachmentPath(s) . - to (String) A single recipient or a comma separated of recipients , like "abc@def.com" or "ghi@jkl.com,mno@pqr.com" . - subject (String) . - text (String) . - attachmentPaths (a String or a list of Strings , Optional) , like "/sdcard/abc.jpg" or [ "/sdcard/def.bmp" , "/sdcard/ghi.png" ] , if this parameter is not None , the "to" parameter may lose efficacy . - extras (Dict Optional) , like { droid.Intent.EXTRA_HTML_TEXT:"abc" } . - wait (Boolean Optional , default to True) , wait for the end of the email send operation . diff --git a/qsl4a-src/androidhelper/doc/intent/sendFile.txt b/qsl4a-src/androidhelper/doc/intent/sendFile.txt deleted file mode 100644 index 9e09561..0000000 --- a/qsl4a-src/androidhelper/doc/intent/sendFile.txt +++ /dev/null @@ -1,6 +0,0 @@ -sendFile(path, type=None, extras=None, wait=True) -Send file(s) with the type and extras with content:// . - path (single String or List of Strings) file path(s) , like "/sdcard/path/to/file.ext" or [ "/sdcard/a/b.txt" , "/sdcard/c/d/e/f.jpg" ] - type (String Optional) MIME-type self-defined - extras (Dict Optional) Extras self-defined, it is not recommended to fill in droid.Intent.EXTRA_STREAM ( "android.intent.extra.STREAM" ) - wait (Boolean Optional) wait for the end of the file send operation diff --git a/qsl4a-src/androidhelper/doc/intent/sendText.txt b/qsl4a-src/androidhelper/doc/intent/sendText.txt deleted file mode 100644 index a7c3fcb..0000000 --- a/qsl4a-src/androidhelper/doc/intent/sendText.txt +++ /dev/null @@ -1,5 +0,0 @@ -sendText(text,extras=None,wait=True) -Start activity with send action by text . - text (String) - extras (Dict Optional) Extras self-defined, it is not recommended to fill in droid.Intent.EXTRA_TEXT ( "android.intent.extra.TEXT" ) - wait (Boolean Optional) wait for the send operation end diff --git a/qsl4a-src/androidhelper/doc/intent/startActivity.txt b/qsl4a-src/androidhelper/doc/intent/startActivity.txt deleted file mode 100644 index ac925fc..0000000 --- a/qsl4a-src/androidhelper/doc/intent/startActivity.txt +++ /dev/null @@ -1,11 +0,0 @@ -startActivity(action,uri=None,type=None,extras=None,wait=None,packagename=None,classname=None,flags=None) -Starts an activity. - action (String) - uri (String) (optional) - type (String) MIME type/subtype of the URI (optional) - extras (JSONObject) a Map of extras to add to the Intent (optional) - wait (Boolean) block until the user exits the started activity (optional) - packagename (String) name of package. If used, requires classname to be useful (optional) - classname (String) name of class. If used, requires packagename to be useful (optional) -packagename and classname, if provided, are used in a 'setComponent' call. - flags (Integer) Intent flags (optional) diff --git a/qsl4a-src/androidhelper/doc/intent/startActivityForResult.txt b/qsl4a-src/androidhelper/doc/intent/startActivityForResult.txt deleted file mode 100644 index d646124..0000000 --- a/qsl4a-src/androidhelper/doc/intent/startActivityForResult.txt +++ /dev/null @@ -1,10 +0,0 @@ -startActivityForResult(action,uri=None,type=None,extras=None,packagename=None,classname=None,flags=None) -Starts an activity and returns the result. - action (String) - uri (String) (optional) - type (String) MIME type/subtype of the URI (optional) - extras (JSONObject) a Map of extras to add to the Intent (optional) - packagename (String) name of package. If used, requires classname to be useful (optional) - classname (String) name of class. If used, requires packagename to be useful (optional) - flags (Integer) Intent flags (optional) -returns: (Intent) A Map representation of the result Intent. diff --git a/qsl4a-src/androidhelper/doc/intent/startActivityForResultIntent.txt b/qsl4a-src/androidhelper/doc/intent/startActivityForResultIntent.txt deleted file mode 100644 index ad5c1e0..0000000 --- a/qsl4a-src/androidhelper/doc/intent/startActivityForResultIntent.txt +++ /dev/null @@ -1,4 +0,0 @@ -startActivityForResultIntent(intent) -Starts an activity and returns the result. - intent (Intent) Intent in the format as returned from makeIntent -returns: (Intent) A Map representation of the result Intent. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/intent/startActivityIntent.txt b/qsl4a-src/androidhelper/doc/intent/startActivityIntent.txt deleted file mode 100644 index a2a7735..0000000 --- a/qsl4a-src/androidhelper/doc/intent/startActivityIntent.txt +++ /dev/null @@ -1,4 +0,0 @@ -startActivityIntent(intent,wait=None) -Start Activity using Intent - intent (Intent) Intent in the format as returned from makeIntent - wait (Boolean) block until the user exits the started activity (optional) \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/intent/takePicture.txt b/qsl4a-src/androidhelper/doc/intent/takePicture.txt deleted file mode 100644 index d581f9a..0000000 --- a/qsl4a-src/androidhelper/doc/intent/takePicture.txt +++ /dev/null @@ -1,4 +0,0 @@ -takePicture(path = None) -Take Picture with system camera . -path (String Optional) if omitted, use default path -return path if success, return None if canceled . \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/intent/takeVideo.txt b/qsl4a-src/androidhelper/doc/intent/takeVideo.txt deleted file mode 100644 index 546beb7..0000000 --- a/qsl4a-src/androidhelper/doc/intent/takeVideo.txt +++ /dev/null @@ -1,5 +0,0 @@ -takeVideo(path = None, quality = 1) -Take Video with system camera . -path (String Optional) if omitted, use default path -quality (Integer Optional) default 1 is high quality, 0 is low quality. -return path if success, return None if canceled . \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/intent/view.txt b/qsl4a-src/androidhelper/doc/intent/view.txt deleted file mode 100644 index 312946f..0000000 --- a/qsl4a-src/androidhelper/doc/intent/view.txt +++ /dev/null @@ -1,6 +0,0 @@ -view(uri,type=None,extras=None,wait=True) -Start activity with view action by URI (i.e. browser, contacts, etc.). - uri (String) - type (String) MIME type/subtype of the URI (optional) - extras (JSONObject) a Map of extras to add to the Intent (optional) - wait (Boolean Optional) wait for the view operation end \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/intent/viewMap.txt b/qsl4a-src/androidhelper/doc/intent/viewMap.txt deleted file mode 100644 index f5cc0e7..0000000 --- a/qsl4a-src/androidhelper/doc/intent/viewMap.txt +++ /dev/null @@ -1,6 +0,0 @@ -viewMap(query,wait=True) -Opens a map search for query . - query (String) : - └ address : "pizza, 123 My Street" ; - └ latitude,longitude : "30,40" for (N30°,E40°) . - wait (Boolean Optional) wait for the map operation end \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/location/getLastKnownLocation.txt b/qsl4a-src/androidhelper/doc/location/getLastKnownLocation.txt deleted file mode 100644 index 324732f..0000000 --- a/qsl4a-src/androidhelper/doc/location/getLastKnownLocation.txt +++ /dev/null @@ -1,3 +0,0 @@ -getLastKnownLocation() -Returns the last known location of the device. -returns: (Map) A map of location information by provider. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/location/getLocale.txt b/qsl4a-src/androidhelper/doc/location/getLocale.txt deleted file mode 100644 index 94aafb6..0000000 --- a/qsl4a-src/androidhelper/doc/location/getLocale.txt +++ /dev/null @@ -1,2 +0,0 @@ -getLocale() -Get system language and country . \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/location/readGnssStatus.txt b/qsl4a-src/androidhelper/doc/location/readGnssStatus.txt deleted file mode 100644 index 5ddc1a1..0000000 --- a/qsl4a-src/androidhelper/doc/location/readGnssStatus.txt +++ /dev/null @@ -1,3 +0,0 @@ -readGnssStatus() -Returns the current Global Navigation Satellite System status , data type is dict in list . -Need Android >= 8 . diff --git a/qsl4a-src/androidhelper/doc/location/readLocation.txt b/qsl4a-src/androidhelper/doc/location/readLocation.txt deleted file mode 100644 index 384ebc4..0000000 --- a/qsl4a-src/androidhelper/doc/location/readLocation.txt +++ /dev/null @@ -1,3 +0,0 @@ -readLocation() -Returns the current location as indicated by all available providers. -returns: (Map) A map of location information by provider. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/location/startLocating.txt b/qsl4a-src/androidhelper/doc/location/startLocating.txt deleted file mode 100644 index afaa223..0000000 --- a/qsl4a-src/androidhelper/doc/location/startLocating.txt +++ /dev/null @@ -1,5 +0,0 @@ -startLocating(minUpdateTime=60000,minUpdateDistance=30,updateGnssStatus=False) -Starts collecting location data. - minUpdateTime (Integer) minimum time between updates in milliseconds (default=60000) , - minUpdateDistance (Integer) minimum distance between updates in meters (default=30) , - updateGnssStatus (Boolean) if Android >= 9 and updateGnssStatus is True (default=False) , update Global Navigation Satellite System status . diff --git a/qsl4a-src/androidhelper/doc/location/stopLocating.txt b/qsl4a-src/androidhelper/doc/location/stopLocating.txt deleted file mode 100644 index 960f60b..0000000 --- a/qsl4a-src/androidhelper/doc/location/stopLocating.txt +++ /dev/null @@ -1,2 +0,0 @@ -stopLocating() -Stops collecting location data. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/media/getMaxMediaVolume.txt b/qsl4a-src/androidhelper/doc/media/getMaxMediaVolume.txt deleted file mode 100644 index e52b83b..0000000 --- a/qsl4a-src/androidhelper/doc/media/getMaxMediaVolume.txt +++ /dev/null @@ -1,2 +0,0 @@ -getMaxMediaVolume() -Returns the maximum media volume. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/media/getMaxRingerVolume.txt b/qsl4a-src/androidhelper/doc/media/getMaxRingerVolume.txt deleted file mode 100644 index cb980fa..0000000 --- a/qsl4a-src/androidhelper/doc/media/getMaxRingerVolume.txt +++ /dev/null @@ -1,2 +0,0 @@ -getMaxRingerVolume() -Returns the maximum ringer volume. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/media/getMediaVolume.txt b/qsl4a-src/androidhelper/doc/media/getMediaVolume.txt deleted file mode 100644 index 6e2b122..0000000 --- a/qsl4a-src/androidhelper/doc/media/getMediaVolume.txt +++ /dev/null @@ -1,2 +0,0 @@ -getMediaVolume() -Returns the current media volume. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/media/getRingerVolume.txt b/qsl4a-src/androidhelper/doc/media/getRingerVolume.txt deleted file mode 100644 index e262fc8..0000000 --- a/qsl4a-src/androidhelper/doc/media/getRingerVolume.txt +++ /dev/null @@ -1,2 +0,0 @@ -getRingerVolume() -Returns the current ringer volume. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/media/getVoiceMailAlphaTag.txt b/qsl4a-src/androidhelper/doc/media/getVoiceMailAlphaTag.txt deleted file mode 100644 index c0836f7..0000000 --- a/qsl4a-src/androidhelper/doc/media/getVoiceMailAlphaTag.txt +++ /dev/null @@ -1,2 +0,0 @@ -getVoiceMailAlphaTag() -Retrieves the alphabetic identifier associated with the voice mail number. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/media/getVoiceMailNumber.txt b/qsl4a-src/androidhelper/doc/media/getVoiceMailNumber.txt deleted file mode 100644 index 739c25d..0000000 --- a/qsl4a-src/androidhelper/doc/media/getVoiceMailNumber.txt +++ /dev/null @@ -1,2 +0,0 @@ -getVoiceMailNumber() -Returns the voice mail number. Return null if it is unavailable. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/media/imageCompress.txt b/qsl4a-src/androidhelper/doc/media/imageCompress.txt deleted file mode 100644 index 4d5656c..0000000 --- a/qsl4a-src/androidhelper/doc/media/imageCompress.txt +++ /dev/null @@ -1,7 +0,0 @@ -imageCompress(srcPath,destPath,targetByteSize=0,targetWidth=0,targetHeight=0) - Compress images based on given parameters . - srcPath (String) source image path - destPath (String) destination image path - targetByteSize (Integer Optional) size of bytes of target image , if omitted to 0 , do not limit size of target image - targetWidth (Integer Optional) width of target image , if omitted to 0 , do not limit width of target image - targetHeight (Integer Optional) height of target image , if omitted to 0 , do not limit height of target image \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/media/imageReaderGetScreenShot.txt b/qsl4a-src/androidhelper/doc/media/imageReaderGetScreenShot.txt deleted file mode 100644 index 843598e..0000000 --- a/qsl4a-src/androidhelper/doc/media/imageReaderGetScreenShot.txt +++ /dev/null @@ -1,5 +0,0 @@ -imageReaderGetScreenShot(path = None, delayMilliSec = 1000) -Get Screen Shot to path . - path (String Optional) jpeg file path, if omitted, use default path - delayMilliSec (Integer Optional) wait delayMilliSec to get Screen Shot -return path \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/media/mediaIsPlaying.txt b/qsl4a-src/androidhelper/doc/media/mediaIsPlaying.txt deleted file mode 100644 index 8464163..0000000 --- a/qsl4a-src/androidhelper/doc/media/mediaIsPlaying.txt +++ /dev/null @@ -1,4 +0,0 @@ -mediaIsPlaying(tag="default") -Checks if media file is playing. - tag (String) string identifying resource (default=default) -returns: (boolean) true if playing \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/media/mediaPlay.txt b/qsl4a-src/androidhelper/doc/media/mediaPlay.txt deleted file mode 100644 index 10de574..0000000 --- a/qsl4a-src/androidhelper/doc/media/mediaPlay.txt +++ /dev/null @@ -1,6 +0,0 @@ -mediaPlay(url,tag="default",play=True) -Open a media file - url (String) url of media resource - tag (String) string identifying resource (default=default) - play (Boolean) start playing immediately (default=true) -returns: (boolean) true if play successful \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/media/mediaPlayClose.txt b/qsl4a-src/androidhelper/doc/media/mediaPlayClose.txt deleted file mode 100644 index 24d07d5..0000000 --- a/qsl4a-src/androidhelper/doc/media/mediaPlayClose.txt +++ /dev/null @@ -1,4 +0,0 @@ -mediaPlayClose(tag="default") -Close media file - tag (String) string identifying resource (default=default) -returns: (boolean) true if successful \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/media/mediaPlayInfo.txt b/qsl4a-src/androidhelper/doc/media/mediaPlayInfo.txt deleted file mode 100644 index eb089a2..0000000 --- a/qsl4a-src/androidhelper/doc/media/mediaPlayInfo.txt +++ /dev/null @@ -1,4 +0,0 @@ -mediaPlayInfo(tag="default") -Information on current media - tag (String) string identifying resource (default=default) -returns: (Map) Media Information \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/media/mediaPlayList.txt b/qsl4a-src/androidhelper/doc/media/mediaPlayList.txt deleted file mode 100644 index fde82e9..0000000 --- a/qsl4a-src/androidhelper/doc/media/mediaPlayList.txt +++ /dev/null @@ -1,3 +0,0 @@ -mediaPlayList() -Lists currently loaded media -returns: (Set) List of Media Tags \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/media/mediaPlayPause.txt b/qsl4a-src/androidhelper/doc/media/mediaPlayPause.txt deleted file mode 100644 index e2ffb9a..0000000 --- a/qsl4a-src/androidhelper/doc/media/mediaPlayPause.txt +++ /dev/null @@ -1,4 +0,0 @@ -mediaPlayPause(tag="default") -pause playing media file - tag (String) string identifying resource (default=default) -returns: (boolean) true if successful \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/media/mediaPlaySeek.txt b/qsl4a-src/androidhelper/doc/media/mediaPlaySeek.txt deleted file mode 100644 index f01a90a..0000000 --- a/qsl4a-src/androidhelper/doc/media/mediaPlaySeek.txt +++ /dev/null @@ -1,5 +0,0 @@ -mediaPlaySeek(msec,tag="default") -Seek To Position - msec (Integer) Position in millseconds - tag (String) string identifying resource (default=default) -returns: (int) New Position (in ms) \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/media/mediaPlaySetLooping.txt b/qsl4a-src/androidhelper/doc/media/mediaPlaySetLooping.txt deleted file mode 100644 index 5ff3c11..0000000 --- a/qsl4a-src/androidhelper/doc/media/mediaPlaySetLooping.txt +++ /dev/null @@ -1,5 +0,0 @@ -mediaPlaySetLooping(enabled=True,tag="default") -Set Looping - enabled (Boolean) (default=true) - tag (String) string identifying resource (default=default) -returns: (boolean) True if successful \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/media/mediaPlayStart.txt b/qsl4a-src/androidhelper/doc/media/mediaPlayStart.txt deleted file mode 100644 index 72147b0..0000000 --- a/qsl4a-src/androidhelper/doc/media/mediaPlayStart.txt +++ /dev/null @@ -1,4 +0,0 @@ -mediaPlayStart(tag="default") -start playing media file - tag (String) string identifying resource (default=default) -returns: (boolean) true if successful \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/media/recordAudio.txt b/qsl4a-src/androidhelper/doc/media/recordAudio.txt deleted file mode 100644 index 398be93..0000000 --- a/qsl4a-src/androidhelper/doc/media/recordAudio.txt +++ /dev/null @@ -1,3 +0,0 @@ -recordAudio() -Record Audio with system soundrecorder . -return path if success, return None if canceled . \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/media/recorderCaptureVideo.txt b/qsl4a-src/androidhelper/doc/media/recorderCaptureVideo.txt deleted file mode 100644 index a186b6d..0000000 --- a/qsl4a-src/androidhelper/doc/media/recorderCaptureVideo.txt +++ /dev/null @@ -1,7 +0,0 @@ -recorderCaptureVideo(targetPath = None, duration = 10, cameraId = 0, quality = 8) -Records video from the camera and saves it to the given location. - targetPath (String Optional) if omitted, use default path - duration (Integer Optional) seconds - cameraId (Integer Optional) - quality (Integer Optional) -returns: targetPath diff --git a/qsl4a-src/androidhelper/doc/media/recorderPause.txt b/qsl4a-src/androidhelper/doc/media/recorderPause.txt deleted file mode 100644 index 36a3cda..0000000 --- a/qsl4a-src/androidhelper/doc/media/recorderPause.txt +++ /dev/null @@ -1,2 +0,0 @@ -recorderPause() -Pause a previously started recording. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/media/recorderResume.txt b/qsl4a-src/androidhelper/doc/media/recorderResume.txt deleted file mode 100644 index b069448..0000000 --- a/qsl4a-src/androidhelper/doc/media/recorderResume.txt +++ /dev/null @@ -1,2 +0,0 @@ -recorderResume() -Resume a previously paused recording. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/media/recorderSoundVolumeDetect.txt b/qsl4a-src/androidhelper/doc/media/recorderSoundVolumeDetect.txt deleted file mode 100644 index 67c85e9..0000000 --- a/qsl4a-src/androidhelper/doc/media/recorderSoundVolumeDetect.txt +++ /dev/null @@ -1,7 +0,0 @@ -recorderSoundVolumeDetect(interval = 100) -Start or Stop to detect Sound Volume Decibel, - interval (Integer Optional) - interval > 0 --> start to detect sound volume decibel according to the time interval (miliseconds), - interval <= 0 --> stop to detect sound volume decibel, - Return None . - If you want to detect Sound Volume Decibel while record sound , you need run recorderStartMicrophone first , then call recorderSoundVolumeDetect . diff --git a/qsl4a-src/androidhelper/doc/media/recorderSoundVolumeGetDb.txt b/qsl4a-src/androidhelper/doc/media/recorderSoundVolumeGetDb.txt deleted file mode 100644 index 6c44520..0000000 --- a/qsl4a-src/androidhelper/doc/media/recorderSoundVolumeGetDb.txt +++ /dev/null @@ -1,4 +0,0 @@ -recorderSoundVolumeGetDb() -Get current Sound Volume Decibel, - Return Sound Volume Decibel float . - Need to run droid.recorderSoundVolumeDetect(interval = 100) first . \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/media/recorderStart.txt b/qsl4a-src/androidhelper/doc/media/recorderStart.txt deleted file mode 100644 index a7aa2c1..0000000 --- a/qsl4a-src/androidhelper/doc/media/recorderStart.txt +++ /dev/null @@ -1,2 +0,0 @@ -recorderStart() -Start a prepared recording. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/media/recorderStartMicrophone.txt b/qsl4a-src/androidhelper/doc/media/recorderStartMicrophone.txt deleted file mode 100644 index a884b0a..0000000 --- a/qsl4a-src/androidhelper/doc/media/recorderStartMicrophone.txt +++ /dev/null @@ -1,4 +0,0 @@ -recorderStartMicrophone(targetPath=None) -Records audio from the microphone and saves it to the given location. - targetPath (String Optional) if omitted, use default path - If you want to detect Sound Volume Decibel while record sound , you need run recorderStartMicrophone first , then call recorderSoundVolumeDetect . diff --git a/qsl4a-src/androidhelper/doc/media/recorderStartScreenRecord.txt b/qsl4a-src/androidhelper/doc/media/recorderStartScreenRecord.txt deleted file mode 100644 index 3ed3b1a..0000000 --- a/qsl4a-src/androidhelper/doc/media/recorderStartScreenRecord.txt +++ /dev/null @@ -1,16 +0,0 @@ -recorderStartScreenRecord ( path = None , audio = recorderStartScreenRecord.MICROPHONE , targetPixels = None , frameRate = 30 , bitRate = None , rotation = False , autoStart = True ) -Record Screen to path . - path (String Optional) mpeg4 file path, if omitted, use default path - audio (Integer Optional) record audio, default is recorderStartScreenRecord.MICROPHONE( integer 1 ) - targetPixels (Integer Optional) target pixels for generated video of record screen , default(None) is ScreenWidth*ScreenHeight - frameRate (Integer Optional) frames per second for generated video of record screen , default is 30 - bitRate (Integer Optional) bit per second for generated video of record screen , default(None) is ScreenWidth*ScreenHeight - rotation (Boolean Optional) width and height rotation exchange - autoStart (Boolean Optional) Auto Start Record after Prepare - { rotation = True and autoStart = False } : { - is for some incompatible devices horizontal screen record, - which can only prepare screen record on the vertical screen, - then switch to the horizontal screen, - then start recording with recorderStart() . - } -return path diff --git a/qsl4a-src/androidhelper/doc/media/recorderStop.txt b/qsl4a-src/androidhelper/doc/media/recorderStop.txt deleted file mode 100644 index ae67543..0000000 --- a/qsl4a-src/androidhelper/doc/media/recorderStop.txt +++ /dev/null @@ -1,2 +0,0 @@ -recorderStop() -Stops a previously started recording. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/media/scanBarcode.txt b/qsl4a-src/androidhelper/doc/media/scanBarcode.txt deleted file mode 100644 index 435dae2..0000000 --- a/qsl4a-src/androidhelper/doc/media/scanBarcode.txt +++ /dev/null @@ -1,4 +0,0 @@ -scanBarcode( title = None ) -Starts the barcode scanner. -returns the result String of Barcode . -title (String Optional) \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/media/scanBarcodeFromImage.txt b/qsl4a-src/androidhelper/doc/media/scanBarcodeFromImage.txt deleted file mode 100644 index 09260d8..0000000 --- a/qsl4a-src/androidhelper/doc/media/scanBarcodeFromImage.txt +++ /dev/null @@ -1,15 +0,0 @@ -scanBarcodeFromImage( path, compressRatio = 0, x = 0, y = 0, width = 0, height = 0) -Scan the QR code from the picture, -path (String): - original Picture path; -compressRatio (Integer Optional): - if a Picture is 1000x800; - if compressRatio == 0 or 1, the Picture will not compress; - if compressRatio == 2, the Picture will compress to 500x400; - if compressRatio == 4, the Picture will compress to 250x200; - and so on; -x or y (Integer Optional): - Position of the original Picture, x==y==0 is the top left corner; -width or height (Integer Optional): - Intercept Size of the original Picture, width==height==0 means original width and height; -returns the result String of Barcode . \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/media/setMediaVolume.txt b/qsl4a-src/androidhelper/doc/media/setMediaVolume.txt deleted file mode 100644 index 8b24ae9..0000000 --- a/qsl4a-src/androidhelper/doc/media/setMediaVolume.txt +++ /dev/null @@ -1,3 +0,0 @@ -setMediaVolume(volume) -Sets the media volume. - volume (Integer) \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/media/setRingerVolume.txt b/qsl4a-src/androidhelper/doc/media/setRingerVolume.txt deleted file mode 100644 index 11bf348..0000000 --- a/qsl4a-src/androidhelper/doc/media/setRingerVolume.txt +++ /dev/null @@ -1,3 +0,0 @@ -setRingerVolume(volume) -Sets the ringer volume. - volume (Integer) \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/media/videoPlay.txt b/qsl4a-src/androidhelper/doc/media/videoPlay.txt deleted file mode 100644 index 71e6b95..0000000 --- a/qsl4a-src/androidhelper/doc/media/videoPlay.txt +++ /dev/null @@ -1,4 +0,0 @@ -videoPlay(path, wait=True) -Play a video file - path (String) video file path - wait (Boolean Optional) wait for the end of video file viewing \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/media/webcamAdjustQuality.txt b/qsl4a-src/androidhelper/doc/media/webcamAdjustQuality.txt deleted file mode 100644 index a49b9d2..0000000 --- a/qsl4a-src/androidhelper/doc/media/webcamAdjustQuality.txt +++ /dev/null @@ -1,4 +0,0 @@ -webcamAdjustQuality(resolutionLevel=0,jpegQuality=20) -Adjusts the quality of the webcam stream while it is running. - resolutionLevel (Integer) increasing this number provides higher resolution (default=0) - jpegQuality (Integer) a number from 0-100 (default=20) \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/media/webcamStart.txt b/qsl4a-src/androidhelper/doc/media/webcamStart.txt deleted file mode 100644 index fe62005..0000000 --- a/qsl4a-src/androidhelper/doc/media/webcamStart.txt +++ /dev/null @@ -1,5 +0,0 @@ -webcamStart(resolutionLevel=0,jpegQuality=20,port=0) -Starts an MJPEG stream and returns a Tuple of address and port for the stream. - resolutionLevel (Integer) increasing this number provides higher resolution (default=0) - jpegQuality (Integer) a number from 0-100 (default=20) - port (Integer) If port is specified, the webcam service will bind to port, otherwise it will pick any available port. (default=0) \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/media/webcamStop.txt b/qsl4a-src/androidhelper/doc/media/webcamStop.txt deleted file mode 100644 index 5db7356..0000000 --- a/qsl4a-src/androidhelper/doc/media/webcamStop.txt +++ /dev/null @@ -1,2 +0,0 @@ -webcamStop() -Stops the webcam stream. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/notify/getVibrateMode.txt b/qsl4a-src/androidhelper/doc/notify/getVibrateMode.txt deleted file mode 100644 index 9e97b00..0000000 --- a/qsl4a-src/androidhelper/doc/notify/getVibrateMode.txt +++ /dev/null @@ -1,4 +0,0 @@ -getVibrateMode(ringer=None) -Checks Vibration setting. If ringer=true then query Ringer setting, else query Notification setting - ringer (Boolean) (optional) -returns: (Boolean) True if vibrate mode is enabled. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/notify/notify.txt b/qsl4a-src/androidhelper/doc/notify/notify.txt deleted file mode 100644 index 74cf706..0000000 --- a/qsl4a-src/androidhelper/doc/notify/notify.txt +++ /dev/null @@ -1,9 +0,0 @@ -notify(title, message, uri=None, arg=None) -Displays a notification that will be disappeared when the user clicks on it. - title (String) - message (String) - uri (String Optional) - arg (String Optional) -If uri is not None, after the user clicks on it, these operation will be doing : - # uri is web url like http:// or https:// --> open system browser - # uri is .py script path --> run .py script with script arg \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/notify/toggleRingerSilentMode.txt b/qsl4a-src/androidhelper/doc/notify/toggleRingerSilentMode.txt deleted file mode 100644 index be1193c..0000000 --- a/qsl4a-src/androidhelper/doc/notify/toggleRingerSilentMode.txt +++ /dev/null @@ -1,4 +0,0 @@ -toggleRingerSilentMode(enabled=None) -Toggles ringer silent mode on and off. - enabled (Boolean) (optional) -returns: (Boolean) True if ringer silent mode is enabled. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/notify/toggleVibrateMode.txt b/qsl4a-src/androidhelper/doc/notify/toggleVibrateMode.txt deleted file mode 100644 index a6b2ef1..0000000 --- a/qsl4a-src/androidhelper/doc/notify/toggleVibrateMode.txt +++ /dev/null @@ -1,5 +0,0 @@ -toggleVibrateMode(enabled=None,ringer=None) -Toggles vibrate mode on and off. If ringer=true then set Ringer setting, else set Notification setting - enabled (Boolean) (optional) - ringer (Boolean) (optional) -returns: (Boolean) True if vibrate mode is enabled. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/notify/vibrate.txt b/qsl4a-src/androidhelper/doc/notify/vibrate.txt deleted file mode 100644 index bf67e31..0000000 --- a/qsl4a-src/androidhelper/doc/notify/vibrate.txt +++ /dev/null @@ -1,3 +0,0 @@ -vibrate(duration=300) -Vibrates the phone or a specified duration in milliseconds. - duration (Integer) duration in milliseconds (default=300) \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/other/backgroundProtect.txt b/qsl4a-src/androidhelper/doc/other/backgroundProtect.txt deleted file mode 100644 index a828532..0000000 --- a/qsl4a-src/androidhelper/doc/other/backgroundProtect.txt +++ /dev/null @@ -1,5 +0,0 @@ -backgroundProtect(enabled = True) -Open the hide suspension window to prevent QPython from being accidentally closed in the background . - Only support Android >= 8.0 . - enabled (Boolean Optional) start or close Background Protect hide suspension window . - Return None . diff --git a/qsl4a-src/androidhelper/doc/other/batteryStartMonitoring.txt b/qsl4a-src/androidhelper/doc/other/batteryStartMonitoring.txt deleted file mode 100644 index 1e1f34a..0000000 --- a/qsl4a-src/androidhelper/doc/other/batteryStartMonitoring.txt +++ /dev/null @@ -1,3 +0,0 @@ -batteryStartMonitoring() -Starts tracking battery state. -throws "battery" events \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/other/checkAirplaneMode.txt b/qsl4a-src/androidhelper/doc/other/checkAirplaneMode.txt deleted file mode 100644 index e40321b..0000000 --- a/qsl4a-src/androidhelper/doc/other/checkAirplaneMode.txt +++ /dev/null @@ -1,3 +0,0 @@ -checkAirplaneMode() -Checks the airplane mode setting. -returns: (Boolean) True if airplane mode is enabled. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/other/checkNetworkRoaming.txt b/qsl4a-src/androidhelper/doc/other/checkNetworkRoaming.txt deleted file mode 100644 index e800eea..0000000 --- a/qsl4a-src/androidhelper/doc/other/checkNetworkRoaming.txt +++ /dev/null @@ -1,2 +0,0 @@ -checkNetworkRoaming() -Returns true if the device is considered roaming on the current network, for GSM purposes. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/other/checkPermissions.txt b/qsl4a-src/androidhelper/doc/other/checkPermissions.txt deleted file mode 100644 index 973767e..0000000 --- a/qsl4a-src/androidhelper/doc/other/checkPermissions.txt +++ /dev/null @@ -1,2 +0,0 @@ -checkPermissions() - Return a dict contains key "granted" and "denied", which indicate the permission that QPython have and not have . \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/other/checkRingerSilentMode.txt b/qsl4a-src/androidhelper/doc/other/checkRingerSilentMode.txt deleted file mode 100644 index 5831995..0000000 --- a/qsl4a-src/androidhelper/doc/other/checkRingerSilentMode.txt +++ /dev/null @@ -1,3 +0,0 @@ -checkRingerSilentMode() -Checks the ringer silent mode setting. -returns: (Boolean) True if ringer silent mode is enabled. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/other/createScriptShortCut.txt b/qsl4a-src/androidhelper/doc/other/createScriptShortCut.txt deleted file mode 100644 index d6add64..0000000 --- a/qsl4a-src/androidhelper/doc/other/createScriptShortCut.txt +++ /dev/null @@ -1,8 +0,0 @@ -createScriptShortCut(scriptPath,label=None,iconPath=None,scriptArg=None) -create Python Script ShortCut, Create ShortCut Permission is Required . - scriptPath (String) - label (String Optional) Text to Show on ShortCut - iconPath (String Optional) Image Path to Show as ShortCut's Icon - scriptArg (String Optional) - return None -Need Android >= 8.0 . \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/other/editorOpen.txt b/qsl4a-src/androidhelper/doc/other/editorOpen.txt deleted file mode 100644 index 84b4415..0000000 --- a/qsl4a-src/androidhelper/doc/other/editorOpen.txt +++ /dev/null @@ -1,4 +0,0 @@ -editorOpen(path) -Open the QPython Editor to display a local Text File , -or open the QPython File Browser to display a local Folder . - path (String) diff --git a/qsl4a-src/androidhelper/doc/other/elapsedRealtimeNanos.txt b/qsl4a-src/androidhelper/doc/other/elapsedRealtimeNanos.txt deleted file mode 100644 index 27ded5a..0000000 --- a/qsl4a-src/androidhelper/doc/other/elapsedRealtimeNanos.txt +++ /dev/null @@ -1,2 +0,0 @@ -elapsedRealtimeNanos() - return Nanoseconds after system startup diff --git a/qsl4a-src/androidhelper/doc/other/environment.txt b/qsl4a-src/androidhelper/doc/other/environment.txt deleted file mode 100644 index ca196e4..0000000 --- a/qsl4a-src/androidhelper/doc/other/environment.txt +++ /dev/null @@ -1,14 +0,0 @@ -environment() -A map of various useful environment details -Map returned: -TZ = Timezone -id = Timezone ID -display = Timezone display name -offset = Offset from UTC (in ms) -SDK = SDK Version -download = default download path -appcache = Location of application cache -sdcard = Space on sdcard -availblocks = Available blocks -blockcount = Total Blocks -blocksize = size of block. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/other/executeQPy.txt b/qsl4a-src/androidhelper/doc/other/executeQPy.txt deleted file mode 100644 index 861fc1c..0000000 --- a/qsl4a-src/androidhelper/doc/other/executeQPy.txt +++ /dev/null @@ -1,5 +0,0 @@ -executeQPy(scriptPath='',arg=None) -Run a QPython script with path . - scriptPath (String) - arg (String Optional) - If scriptPath is empty, open python shell only . \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/other/generateDtmfTones.txt b/qsl4a-src/androidhelper/doc/other/generateDtmfTones.txt deleted file mode 100644 index b6bc7f6..0000000 --- a/qsl4a-src/androidhelper/doc/other/generateDtmfTones.txt +++ /dev/null @@ -1,4 +0,0 @@ -generateDtmfTones(phoneNumber,toneDuration=100) -Generate DTMF tones for the given phone number. - phoneNumber (String) - toneDuration (Integer) duration of each tone in milliseconds (default=100) \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/other/geocode.txt b/qsl4a-src/androidhelper/doc/other/geocode.txt deleted file mode 100644 index b80cf77..0000000 --- a/qsl4a-src/androidhelper/doc/other/geocode.txt +++ /dev/null @@ -1,6 +0,0 @@ -geocode(latitude,longitude,maxResults=1) -Returns a list of addresses for the given latitude and longitude. - latitude (Double) - longitude (Double) - maxResults (Integer) maximum number of results (default=1) -returns: (List) A list of addresses. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/other/getAndroidID.txt b/qsl4a-src/androidhelper/doc/other/getAndroidID.txt deleted file mode 100644 index b0a666b..0000000 --- a/qsl4a-src/androidhelper/doc/other/getAndroidID.txt +++ /dev/null @@ -1,2 +0,0 @@ -getAndroidID() -Returns Android ID . \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/other/getApplicationInfo.txt b/qsl4a-src/androidhelper/doc/other/getApplicationInfo.txt deleted file mode 100644 index b52f330..0000000 --- a/qsl4a-src/androidhelper/doc/other/getApplicationInfo.txt +++ /dev/null @@ -1,5 +0,0 @@ -getApplicationInfo(packageName = None) -Get Application Information with Package Name . - packageName (String Optional) - If omitted, use QPython Package Name . - If Package Name not exists , android.content.pm.PackageManager$NameNotFoundException will be ocured . diff --git a/qsl4a-src/androidhelper/doc/other/getConstants.txt b/qsl4a-src/androidhelper/doc/other/getConstants.txt deleted file mode 100644 index 3fda091..0000000 --- a/qsl4a-src/androidhelper/doc/other/getConstants.txt +++ /dev/null @@ -1,3 +0,0 @@ -getConstants(classname) -Get list of constants (static final fields) for a class - classname (String) Class to get constants from \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/other/getDeviceSoftwareVersion.txt b/qsl4a-src/androidhelper/doc/other/getDeviceSoftwareVersion.txt deleted file mode 100644 index 5f6a7e6..0000000 --- a/qsl4a-src/androidhelper/doc/other/getDeviceSoftwareVersion.txt +++ /dev/null @@ -1,2 +0,0 @@ -getDeviceSoftwareVersion() -Returns the software version number for the device, for example, the IMEI/SV for GSM phones. Return null if the software version is not available. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/other/getFileStat.txt b/qsl4a-src/androidhelper/doc/other/getFileStat.txt deleted file mode 100644 index 41cc493..0000000 --- a/qsl4a-src/androidhelper/doc/other/getFileStat.txt +++ /dev/null @@ -1,3 +0,0 @@ -getFileStat(path) -Return a dict of file length , last modified , file can read/write and disk space . - path (String) diff --git a/qsl4a-src/androidhelper/doc/other/getHarmonyOsInformation.txt b/qsl4a-src/androidhelper/doc/other/getHarmonyOsInformation.txt deleted file mode 100644 index c1d336e..0000000 --- a/qsl4a-src/androidhelper/doc/other/getHarmonyOsInformation.txt +++ /dev/null @@ -1,2 +0,0 @@ -getHarmonyOsInformation() -Returns a dict of isHarmonyOs and HarmonyVersion(if isHarmonyOs is true) . \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/other/getLastLog.txt b/qsl4a-src/androidhelper/doc/other/getLastLog.txt deleted file mode 100644 index 1b142e7..0000000 --- a/qsl4a-src/androidhelper/doc/other/getLastLog.txt +++ /dev/null @@ -1,2 +0,0 @@ -getLastLog(logFileName='last.log') - Return Log with Log File Name \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/other/getMemoryInfo.txt b/qsl4a-src/androidhelper/doc/other/getMemoryInfo.txt deleted file mode 100644 index 6a70142..0000000 --- a/qsl4a-src/androidhelper/doc/other/getMemoryInfo.txt +++ /dev/null @@ -1,2 +0,0 @@ -getMemoryInfo() -get Memory Information diff --git a/qsl4a-src/androidhelper/doc/other/getSubscriberId.txt b/qsl4a-src/androidhelper/doc/other/getSubscriberId.txt deleted file mode 100644 index 88f4b3c..0000000 --- a/qsl4a-src/androidhelper/doc/other/getSubscriberId.txt +++ /dev/null @@ -1,2 +0,0 @@ -getSubscriberId() -Returns the unique subscriber ID, for example, the IMSI for a GSM phone. Return null if it is unavailable. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/other/getSysInfo.txt b/qsl4a-src/androidhelper/doc/other/getSysInfo.txt deleted file mode 100644 index 57932ba..0000000 --- a/qsl4a-src/androidhelper/doc/other/getSysInfo.txt +++ /dev/null @@ -1,2 +0,0 @@ -getSysInfo() -get system information diff --git a/qsl4a-src/androidhelper/doc/other/isExternalStorageManager.txt b/qsl4a-src/androidhelper/doc/other/isExternalStorageManager.txt deleted file mode 100644 index 8fdad9a..0000000 --- a/qsl4a-src/androidhelper/doc/other/isExternalStorageManager.txt +++ /dev/null @@ -1,3 +0,0 @@ -isExternalStorageManager() -return None if Android <= 10 . -return QPython is External Storage Manager (True or False) if Android >= 11 . diff --git a/qsl4a-src/androidhelper/doc/other/log.txt b/qsl4a-src/androidhelper/doc/other/log.txt deleted file mode 100644 index d4d7215..0000000 --- a/qsl4a-src/androidhelper/doc/other/log.txt +++ /dev/null @@ -1,3 +0,0 @@ -log(message) -Writes message to logcat. - message (String) \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/other/makeToast.txt b/qsl4a-src/androidhelper/doc/other/makeToast.txt deleted file mode 100644 index 2a15953..0000000 --- a/qsl4a-src/androidhelper/doc/other/makeToast.txt +++ /dev/null @@ -1,10 +0,0 @@ -makeToast(message,length=makeToast.SHORT,isHtml=False,backColor=None,textColor=None,textSize=0,textAlign=0) -Displays a Toast notification. - message (String) - length (Integer Optional) SHORT is 2 second , LONG is 3.5 second - isHtml (Boolean Optional) Indicates that the message is html, the values are True (HTML, HyperText Markup Language) / False (Default, Plain Text with new Toast) / None (Plain Text with original Toast, Background support) - If isHtml is not None, you can also use these parameters, but not support Background: - backColor (String Optional) default is 'efefefef' - textColor (String Optional) default is 'ff000000' - textSize (Interger Optional) default determined by the system - textAlign (Interger Optional) default determined by the system diff --git a/qsl4a-src/androidhelper/doc/other/notebookOpen.txt b/qsl4a-src/androidhelper/doc/other/notebookOpen.txt deleted file mode 100644 index 59d48f2..0000000 --- a/qsl4a-src/androidhelper/doc/other/notebookOpen.txt +++ /dev/null @@ -1,4 +0,0 @@ -notebookOpen(path = None) - Open the path with Jupyter Notebook . - path (String) can be .ipynb/.py/.txt files or folders , but path must in sdcard . - if path is omitted , will open Jupyter Notebook Home Page . \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/other/queryAttributes.txt b/qsl4a-src/androidhelper/doc/other/queryAttributes.txt deleted file mode 100644 index 351548e..0000000 --- a/qsl4a-src/androidhelper/doc/other/queryAttributes.txt +++ /dev/null @@ -1,4 +0,0 @@ -queryAttributes(uri) -Content Resolver Query Attributes - uri (String) The URI, using the content:// scheme, for the content to retrieve. -returns: (JSONArray) a list of available columns for a given content uri \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/other/queryContent.txt b/qsl4a-src/androidhelper/doc/other/queryContent.txt deleted file mode 100644 index 610c358..0000000 --- a/qsl4a-src/androidhelper/doc/other/queryContent.txt +++ /dev/null @@ -1,9 +0,0 @@ -queryContent(uri,attributes=None,selection=None,selectionArgs=None,order=None) -Content Resolver Query - uri (String) The URI, using the content:// scheme, for the content to retrieve. - attributes (JSONArray) A list of which columns to return. Passing null will return all columns (optional) - selection (String) A filter declaring which rows to return (optional) - selectionArgs (JSONArray) You may include ?s in selection, which will be replaced by the values from selectionArgs (optional) - order (String) How to order the rows (optional) -returns: (List) result of query as Maps -Exactly as per ContentResolver.query \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/other/requestPermissions.txt b/qsl4a-src/androidhelper/doc/other/requestPermissions.txt deleted file mode 100644 index b995436..0000000 --- a/qsl4a-src/androidhelper/doc/other/requestPermissions.txt +++ /dev/null @@ -1,3 +0,0 @@ -requestPermissions(permissions = None) - Request Permissions that QPython do not have, some permission cannot be granted succesfully, - permissions (List of String, Optional) if permissions is None, use all denied permissions . \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/package/forceStopPackage.txt b/qsl4a-src/androidhelper/doc/package/forceStopPackage.txt deleted file mode 100644 index fc71f3c..0000000 --- a/qsl4a-src/androidhelper/doc/package/forceStopPackage.txt +++ /dev/null @@ -1,3 +0,0 @@ -forceStopPackage(packageName) -Force stops a package. - packageName (String) name of package \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/package/getInstalledPackages.txt b/qsl4a-src/androidhelper/doc/package/getInstalledPackages.txt deleted file mode 100644 index e17a8f4..0000000 --- a/qsl4a-src/androidhelper/doc/package/getInstalledPackages.txt +++ /dev/null @@ -1,8 +0,0 @@ -getInstalledPackages(flag = 4) -Returns a list of installed packages with application name . - flag (Integer Optional) : - 5 - All apps - 4 - User apps (default) - 3 - System apps - 2 - Updated System apps - 1 - Not Updated System apps diff --git a/qsl4a-src/androidhelper/doc/package/getLaunchablePackages.txt b/qsl4a-src/androidhelper/doc/package/getLaunchablePackages.txt deleted file mode 100644 index 40c98bc..0000000 --- a/qsl4a-src/androidhelper/doc/package/getLaunchablePackages.txt +++ /dev/null @@ -1,3 +0,0 @@ -getLaunchablePackages(needClassName=False) -If needClassName is True , returns a list of all launchable packages with class name and application name . -If needClassName is False default , returns a list of all launchable packages with application name . \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/package/getPackageVersion.txt b/qsl4a-src/androidhelper/doc/package/getPackageVersion.txt deleted file mode 100644 index 1270433..0000000 --- a/qsl4a-src/androidhelper/doc/package/getPackageVersion.txt +++ /dev/null @@ -1,3 +0,0 @@ -getPackageVersion(packageName) -Returns package version name. - packageName (String) \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/package/getPackageVersionCode.txt b/qsl4a-src/androidhelper/doc/package/getPackageVersionCode.txt deleted file mode 100644 index d2f55f6..0000000 --- a/qsl4a-src/androidhelper/doc/package/getPackageVersionCode.txt +++ /dev/null @@ -1,3 +0,0 @@ -getPackageVersionCode(packageName) -Returns package version code. - packageName (String) \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/package/getRunningPackages.txt b/qsl4a-src/androidhelper/doc/package/getRunningPackages.txt deleted file mode 100644 index 2475367..0000000 --- a/qsl4a-src/androidhelper/doc/package/getRunningPackages.txt +++ /dev/null @@ -1,3 +0,0 @@ -getRunningPackages() -Returns a list of packages running activities or services. -returns: (List) List of packages running activities. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/phone/getAllCellInfo.txt b/qsl4a-src/androidhelper/doc/phone/getAllCellInfo.txt deleted file mode 100644 index 7ce4fe7..0000000 --- a/qsl4a-src/androidhelper/doc/phone/getAllCellInfo.txt +++ /dev/null @@ -1,2 +0,0 @@ -getAllCellInfo() -Returns all the neighboring cell information of the device. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/phone/getAllCellsLocation.txt b/qsl4a-src/androidhelper/doc/phone/getAllCellsLocation.txt deleted file mode 100644 index 6a300e1..0000000 --- a/qsl4a-src/androidhelper/doc/phone/getAllCellsLocation.txt +++ /dev/null @@ -1,2 +0,0 @@ -getAllCellsLocation() -Returns all the cells location : LAC/CID/SID/NID/BID/MccMnc/Arcfn and signal dbm of NR/LTE/WCDMA/CDMA/GSM . \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/phone/getCellLocation.txt b/qsl4a-src/androidhelper/doc/phone/getCellLocation.txt deleted file mode 100644 index f91ed92..0000000 --- a/qsl4a-src/androidhelper/doc/phone/getCellLocation.txt +++ /dev/null @@ -1,3 +0,0 @@ -getCellLocation() -Returns the current cell location : LAC/CID/SID/NID/BID and signal dbm of NR/LTE/WCDMA/CDMA/GSM , -and cells count around . \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/phone/getImei.txt b/qsl4a-src/androidhelper/doc/phone/getImei.txt deleted file mode 100644 index f45372c..0000000 --- a/qsl4a-src/androidhelper/doc/phone/getImei.txt +++ /dev/null @@ -1,5 +0,0 @@ -getImei( slotIndex = None ) -Returns IMEI for GSM phones . -Return null if IMEI is not available. -slotIndex (Integer Optional) -If Android <= 7.1, return legacy deviceId . \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/phone/getLine1Number.txt b/qsl4a-src/androidhelper/doc/phone/getLine1Number.txt deleted file mode 100644 index 658bed7..0000000 --- a/qsl4a-src/androidhelper/doc/phone/getLine1Number.txt +++ /dev/null @@ -1,2 +0,0 @@ -getLine1Number() -Returns the phone number string for line 1, for example, the MSISDN for a GSM phone. Return null if it is unavailable. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/phone/getMeid.txt b/qsl4a-src/androidhelper/doc/phone/getMeid.txt deleted file mode 100644 index df65766..0000000 --- a/qsl4a-src/androidhelper/doc/phone/getMeid.txt +++ /dev/null @@ -1,5 +0,0 @@ -getMeid( slotIndex = None ) -Returns MEID for CDMA phones . -Return null if MEID is not available. -slotIndex (Integer Optional) -If Android <= 7.1, return legacy deviceId . \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/phone/getNetworkOperator.txt b/qsl4a-src/androidhelper/doc/phone/getNetworkOperator.txt deleted file mode 100644 index 8a72a7a..0000000 --- a/qsl4a-src/androidhelper/doc/phone/getNetworkOperator.txt +++ /dev/null @@ -1,2 +0,0 @@ -getNetworkOperator() -Returns the numeric name (MCC+MNC) of current registered operator. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/phone/getNetworkOperatorName.txt b/qsl4a-src/androidhelper/doc/phone/getNetworkOperatorName.txt deleted file mode 100644 index 7c61bf9..0000000 --- a/qsl4a-src/androidhelper/doc/phone/getNetworkOperatorName.txt +++ /dev/null @@ -1,2 +0,0 @@ -getNetworkOperatorName() -Returns the alphabetic name of current registered operator. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/phone/getNetworkType.txt b/qsl4a-src/androidhelper/doc/phone/getNetworkType.txt deleted file mode 100644 index 5e1c5b6..0000000 --- a/qsl4a-src/androidhelper/doc/phone/getNetworkType.txt +++ /dev/null @@ -1,2 +0,0 @@ -getNetworkType() -Returns a the radio technology (network type) currently in use on the device. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/phone/getPhoneType.txt b/qsl4a-src/androidhelper/doc/phone/getPhoneType.txt deleted file mode 100644 index 56e4ae1..0000000 --- a/qsl4a-src/androidhelper/doc/phone/getPhoneType.txt +++ /dev/null @@ -1,2 +0,0 @@ -getPhoneType() -Returns the device phone type. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/phone/getSimCountryIso.txt b/qsl4a-src/androidhelper/doc/phone/getSimCountryIso.txt deleted file mode 100644 index fd0eadc..0000000 --- a/qsl4a-src/androidhelper/doc/phone/getSimCountryIso.txt +++ /dev/null @@ -1,2 +0,0 @@ -getSimCountryIso() -Returns the ISO country code equivalent for the SIM provider\'s country code. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/phone/getSimOperator.txt b/qsl4a-src/androidhelper/doc/phone/getSimOperator.txt deleted file mode 100644 index 65f9e64..0000000 --- a/qsl4a-src/androidhelper/doc/phone/getSimOperator.txt +++ /dev/null @@ -1,2 +0,0 @@ -getSimOperator() -Returns the MCC+MNC (mobile country code + mobile network code) of the provider of the SIM. 5 or 6 decimal digits. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/phone/getSimOperatorName.txt b/qsl4a-src/androidhelper/doc/phone/getSimOperatorName.txt deleted file mode 100644 index b881373..0000000 --- a/qsl4a-src/androidhelper/doc/phone/getSimOperatorName.txt +++ /dev/null @@ -1,2 +0,0 @@ -getSimOperatorName() -Returns the Service Provider Name (SPN). \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/phone/getSimSerialNumber.txt b/qsl4a-src/androidhelper/doc/phone/getSimSerialNumber.txt deleted file mode 100644 index d99bd54..0000000 --- a/qsl4a-src/androidhelper/doc/phone/getSimSerialNumber.txt +++ /dev/null @@ -1,2 +0,0 @@ -getSimSerialNumber() -Returns the serial number of the SIM, if applicable. Return null if it is unavailable. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/phone/getSimState.txt b/qsl4a-src/androidhelper/doc/phone/getSimState.txt deleted file mode 100644 index 9b09e35..0000000 --- a/qsl4a-src/androidhelper/doc/phone/getSimState.txt +++ /dev/null @@ -1,2 +0,0 @@ -getSimState() -Returns the state of the device SIM card. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/phone/getTelephoneSignalStrengthDetail.txt b/qsl4a-src/androidhelper/doc/phone/getTelephoneSignalStrengthDetail.txt deleted file mode 100644 index 21a0b11..0000000 --- a/qsl4a-src/androidhelper/doc/phone/getTelephoneSignalStrengthDetail.txt +++ /dev/null @@ -1,4 +0,0 @@ -getTelephoneSignalStrengthDetail() -Return Signal Strength Detail Information, - like RSRP, RSRQ, SNR . - need Android >= 9.0 . \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/phone/getTelephoneSignalStrengthLevel.txt b/qsl4a-src/androidhelper/doc/phone/getTelephoneSignalStrengthLevel.txt deleted file mode 100644 index 9302ba0..0000000 --- a/qsl4a-src/androidhelper/doc/phone/getTelephoneSignalStrengthLevel.txt +++ /dev/null @@ -1,4 +0,0 @@ -getTelephoneSignalStrengthLevel() -Return Signal Strength Level, - for different models have (0 to 5) or (0 to 4) , - need Android >= 9.0 . \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/phone/phoneCall.txt b/qsl4a-src/androidhelper/doc/phone/phoneCall.txt deleted file mode 100644 index 0d8d4fa..0000000 --- a/qsl4a-src/androidhelper/doc/phone/phoneCall.txt +++ /dev/null @@ -1,3 +0,0 @@ -phoneCall(uri) -Calls a contact/phone number by URI. - uri (String) \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/phone/phoneCallNumber.txt b/qsl4a-src/androidhelper/doc/phone/phoneCallNumber.txt deleted file mode 100644 index 3dbe132..0000000 --- a/qsl4a-src/androidhelper/doc/phone/phoneCallNumber.txt +++ /dev/null @@ -1,3 +0,0 @@ -phoneCallNumber(phone_number) -Calls a phone number. - phone number (String) \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/phone/phoneDial.txt b/qsl4a-src/androidhelper/doc/phone/phoneDial.txt deleted file mode 100644 index b53e9ea..0000000 --- a/qsl4a-src/androidhelper/doc/phone/phoneDial.txt +++ /dev/null @@ -1,3 +0,0 @@ -phoneDial(uri) -Dials a contact/phone number by URI. - uri (String) \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/phone/phoneDialNumber.txt b/qsl4a-src/androidhelper/doc/phone/phoneDialNumber.txt deleted file mode 100644 index 190a364..0000000 --- a/qsl4a-src/androidhelper/doc/phone/phoneDialNumber.txt +++ /dev/null @@ -1,3 +0,0 @@ -phoneDialNumber(phone_number) -Dials a phone number. - phone number (String) \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/phone/pickPhone.txt b/qsl4a-src/androidhelper/doc/phone/pickPhone.txt deleted file mode 100644 index ba0d21f..0000000 --- a/qsl4a-src/androidhelper/doc/phone/pickPhone.txt +++ /dev/null @@ -1,3 +0,0 @@ -pickPhone() -Displays a list of phone numbers to pick from. -returns: (String) The selected phone number. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/phone/readPhoneState.txt b/qsl4a-src/androidhelper/doc/phone/readPhoneState.txt deleted file mode 100644 index 2cc678b..0000000 --- a/qsl4a-src/androidhelper/doc/phone/readPhoneState.txt +++ /dev/null @@ -1,3 +0,0 @@ -readPhoneState() -Returns the current phone state and incoming number. -returns: (Bundle) A Map of \"state\" and \"incomingNumber\" \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/phone/readSignalStrengths.txt b/qsl4a-src/androidhelper/doc/phone/readSignalStrengths.txt deleted file mode 100644 index fab9604..0000000 --- a/qsl4a-src/androidhelper/doc/phone/readSignalStrengths.txt +++ /dev/null @@ -1,3 +0,0 @@ -readSignalStrengths() -Returns the current signal strengths. GSM/CDMA/EVDO/LTE -returns: (Bundle) A map of "signal_strength" diff --git a/qsl4a-src/androidhelper/doc/phone/startTrackingPhoneState.txt b/qsl4a-src/androidhelper/doc/phone/startTrackingPhoneState.txt deleted file mode 100644 index 0455276..0000000 --- a/qsl4a-src/androidhelper/doc/phone/startTrackingPhoneState.txt +++ /dev/null @@ -1,2 +0,0 @@ -startTrackingPhoneState() -Starts tracking phone state. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/phone/startTrackingSignalStrengths.txt b/qsl4a-src/androidhelper/doc/phone/startTrackingSignalStrengths.txt deleted file mode 100644 index 9c9f4d6..0000000 --- a/qsl4a-src/androidhelper/doc/phone/startTrackingSignalStrengths.txt +++ /dev/null @@ -1,2 +0,0 @@ -startTrackingSignalStrengths() -Starts tracking signal strengths. GSM/CDMA/EVDO \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/phone/stopTrackingPhoneState.txt b/qsl4a-src/androidhelper/doc/phone/stopTrackingPhoneState.txt deleted file mode 100644 index 625b222..0000000 --- a/qsl4a-src/androidhelper/doc/phone/stopTrackingPhoneState.txt +++ /dev/null @@ -1,2 +0,0 @@ -stopTrackingPhoneState() -Stops tracking phone state. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/phone/stopTrackingSignalStrengths.txt b/qsl4a-src/androidhelper/doc/phone/stopTrackingSignalStrengths.txt deleted file mode 100644 index 218727e..0000000 --- a/qsl4a-src/androidhelper/doc/phone/stopTrackingSignalStrengths.txt +++ /dev/null @@ -1,2 +0,0 @@ -stopTrackingSignalStrengths() -Stops tracking signal strength. GSM/CDMA/EVDO \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/phone/toggleAirplaneMode.txt b/qsl4a-src/androidhelper/doc/phone/toggleAirplaneMode.txt deleted file mode 100644 index 00a3b09..0000000 --- a/qsl4a-src/androidhelper/doc/phone/toggleAirplaneMode.txt +++ /dev/null @@ -1,4 +0,0 @@ -toggleAirplaneMode(enabled=None) -Toggles airplane mode on and off. - enabled (Boolean) (optional) -returns: (Boolean) True if airplane mode is enabled. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/pref/prefGetAll.txt b/qsl4a-src/androidhelper/doc/pref/prefGetAll.txt deleted file mode 100644 index fff6de8..0000000 --- a/qsl4a-src/androidhelper/doc/pref/prefGetAll.txt +++ /dev/null @@ -1,4 +0,0 @@ -prefGetAll(filename=None) -Get list of Shared Preference Values - filename (String) Desired preferences file. If not defined, uses the default Shared Preferences. (optional) -returns: (Map) Map of key,value \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/pref/prefGetValue.txt b/qsl4a-src/androidhelper/doc/pref/prefGetValue.txt deleted file mode 100644 index faf6fd4..0000000 --- a/qsl4a-src/androidhelper/doc/pref/prefGetValue.txt +++ /dev/null @@ -1,4 +0,0 @@ -prefGetValue(key,filename=None) -Read a value from shared preferences - key (String) - filename (String) Desired preferences file. If not defined, uses the default Shared Preferences. (optional) \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/pref/prefPutValue.txt b/qsl4a-src/androidhelper/doc/pref/prefPutValue.txt deleted file mode 100644 index 5d87ada..0000000 --- a/qsl4a-src/androidhelper/doc/pref/prefPutValue.txt +++ /dev/null @@ -1,5 +0,0 @@ -prefPutValue(key,value,filename=None) -Write a value to shared preferences - key (String) - value (Object) - filename (String) Desired preferences file. If not defined, uses the default Shared Preferences. (optional) \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/pref/prefRemoveValue.txt b/qsl4a-src/androidhelper/doc/pref/prefRemoveValue.txt deleted file mode 100644 index 8bed3a0..0000000 --- a/qsl4a-src/androidhelper/doc/pref/prefRemoveValue.txt +++ /dev/null @@ -1,4 +0,0 @@ -prefRemoveValue(key,filename=None) -Remove a value from shared preferences - key (String) - filename (String) Desired preferences file. If not defined, uses the default Shared Preferences. (optional) diff --git a/qsl4a-src/androidhelper/doc/screen/checkScreenOn.txt b/qsl4a-src/androidhelper/doc/screen/checkScreenOn.txt deleted file mode 100644 index 8cb5533..0000000 --- a/qsl4a-src/androidhelper/doc/screen/checkScreenOn.txt +++ /dev/null @@ -1,3 +0,0 @@ -checkScreenOn() -Checks if the screen is on or off (requires API level 7). -returns: (Boolean) True if the screen is currently on. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/screen/getScreenBrightness.txt b/qsl4a-src/androidhelper/doc/screen/getScreenBrightness.txt deleted file mode 100644 index 79ade1f..0000000 --- a/qsl4a-src/androidhelper/doc/screen/getScreenBrightness.txt +++ /dev/null @@ -1,3 +0,0 @@ -getScreenBrightness() -Returns the screen backlight brightness. -returns: (Integer) the current screen brightness between 0 and 255 \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/screen/getScreenInfo.txt b/qsl4a-src/androidhelper/doc/screen/getScreenInfo.txt deleted file mode 100644 index 6b07b09..0000000 --- a/qsl4a-src/androidhelper/doc/screen/getScreenInfo.txt +++ /dev/null @@ -1,2 +0,0 @@ -getScreenInfo() -get screen information diff --git a/qsl4a-src/androidhelper/doc/screen/getScreenTimeout.txt b/qsl4a-src/androidhelper/doc/screen/getScreenTimeout.txt deleted file mode 100644 index 3f1a86d..0000000 --- a/qsl4a-src/androidhelper/doc/screen/getScreenTimeout.txt +++ /dev/null @@ -1,3 +0,0 @@ -getScreenTimeout() -Returns the current screen timeout in seconds. -returns: (Integer) the current screen timeout in seconds. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/screen/setScreenBrightness.txt b/qsl4a-src/androidhelper/doc/screen/setScreenBrightness.txt deleted file mode 100644 index 67f5f6d..0000000 --- a/qsl4a-src/androidhelper/doc/screen/setScreenBrightness.txt +++ /dev/null @@ -1,5 +0,0 @@ -setScreenBrightness(value=None) -Sets the the screen backlight brightness. - value (Integer) brightness value between 0 and 255, - if value defaults to None, use automatic brightness . -returns: (Integer) the original screen brightness. diff --git a/qsl4a-src/androidhelper/doc/screen/setScreenTimeout.txt b/qsl4a-src/androidhelper/doc/screen/setScreenTimeout.txt deleted file mode 100644 index abe04e3..0000000 --- a/qsl4a-src/androidhelper/doc/screen/setScreenTimeout.txt +++ /dev/null @@ -1,4 +0,0 @@ -setScreenTimeout(value) -Sets the screen timeout to this number of seconds. - value (Integer) -returns: (Integer) The original screen timeout. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/screen/showScreenLock.txt b/qsl4a-src/androidhelper/doc/screen/showScreenLock.txt deleted file mode 100644 index 9afee5e..0000000 --- a/qsl4a-src/androidhelper/doc/screen/showScreenLock.txt +++ /dev/null @@ -1,4 +0,0 @@ -showScreenLock() -Show Screen Lock Verify with password or fingerprint . - return True if verify ok , - return False if verify fail . diff --git a/qsl4a-src/androidhelper/doc/sensing/startSensingThreshold.txt b/qsl4a-src/androidhelper/doc/sensing/startSensingThreshold.txt deleted file mode 100644 index dc61595..0000000 --- a/qsl4a-src/androidhelper/doc/sensing/startSensingThreshold.txt +++ /dev/null @@ -1,5 +0,0 @@ -startSensingThreshold(sensorNumber,threshold,axis) -Records to the Event Queue sensor data exceeding a chosen threshold. - sensorNumber (Integer) 1 = All, 2 = Accelerometer, 3 = Magnetometer, 4 = Light, 5 = StepCounter, 6 = Gyroscope - threshold (Integer) Threshold level for chosen sensor (integer) - axis (Integer) 0 = No axis, 1 = X, 2 = Y, 3 = X+Y, 4 = Z, 5= X+Z, 6 = Y+Z, 7 = X+Y+Z diff --git a/qsl4a-src/androidhelper/doc/sensing/startSensingTimed.txt b/qsl4a-src/androidhelper/doc/sensing/startSensingTimed.txt deleted file mode 100644 index 8c67b17..0000000 --- a/qsl4a-src/androidhelper/doc/sensing/startSensingTimed.txt +++ /dev/null @@ -1,4 +0,0 @@ -startSensingTimed(sensorNumber,delayTime) -Starts recording sensor data to be available for polling. - sensorNumber (Integer) 1 = All, 2 = Accelerometer, 3 = Magnetometer, 4 = Light, 5 = StepCounter, 6 = Gyroscope - delayTime (Integer) Minimum time between readings in milliseconds diff --git a/qsl4a-src/androidhelper/doc/sharedVariable/sharedVariableGet.txt b/qsl4a-src/androidhelper/doc/sharedVariable/sharedVariableGet.txt deleted file mode 100644 index 3acd8cc..0000000 --- a/qsl4a-src/androidhelper/doc/sharedVariable/sharedVariableGet.txt +++ /dev/null @@ -1,6 +0,0 @@ -sharedVariableGet(key) -Get Shared Variable of SL4A which can be use by other QPython scripts . - key (String) -Return the value string corresponding to the key string . -If the key does not exist, an error will be reported. -Note: when qpython is closed, these shared variables will be cleared. diff --git a/qsl4a-src/androidhelper/doc/sharedVariable/sharedVariableRemove.txt b/qsl4a-src/androidhelper/doc/sharedVariable/sharedVariableRemove.txt deleted file mode 100644 index 790b3d1..0000000 --- a/qsl4a-src/androidhelper/doc/sharedVariable/sharedVariableRemove.txt +++ /dev/null @@ -1,6 +0,0 @@ -sharedVariableRemove(key) -remove Shared Variable of SL4A which will cannot use by other QPython scripts . - key (String) which need to remove -If the key exists, return the original value string corresponding to the key string , -If the key not exist, return None . -note: when qpython is closed, these shared variables will be cleared. diff --git a/qsl4a-src/androidhelper/doc/sharedVariable/sharedVariableSet.txt b/qsl4a-src/androidhelper/doc/sharedVariable/sharedVariableSet.txt deleted file mode 100644 index 3fa1463..0000000 --- a/qsl4a-src/androidhelper/doc/sharedVariable/sharedVariableSet.txt +++ /dev/null @@ -1,7 +0,0 @@ -sharedVariableSet(key, value) -set Shared Variable of SL4A which can be use by other QPython scripts . - key (String) - value (String) -If the key exists, return the original value string corresponding to the key string , -If the key not exist, return None . -note: when qpython is closed, these shared variables will be cleared. diff --git a/qsl4a-src/androidhelper/doc/sms/smsDeleteMessage.txt b/qsl4a-src/androidhelper/doc/sms/smsDeleteMessage.txt deleted file mode 100644 index 7db74ec..0000000 --- a/qsl4a-src/androidhelper/doc/sms/smsDeleteMessage.txt +++ /dev/null @@ -1,4 +0,0 @@ -smsDeleteMessage(id) -Deletes a message. - id (Integer) -returns: (Boolean) True if the message was deleted \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/sms/smsGetAttributes.txt b/qsl4a-src/androidhelper/doc/sms/smsGetAttributes.txt deleted file mode 100644 index 86372b6..0000000 --- a/qsl4a-src/androidhelper/doc/sms/smsGetAttributes.txt +++ /dev/null @@ -1,2 +0,0 @@ -smsGetAttributes() -Returns a List of all possible message attributes. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/sms/smsGetMessageById.txt b/qsl4a-src/androidhelper/doc/sms/smsGetMessageById.txt deleted file mode 100644 index 1ee7873..0000000 --- a/qsl4a-src/androidhelper/doc/sms/smsGetMessageById.txt +++ /dev/null @@ -1,4 +0,0 @@ -smsGetMessageById(id,attributes=None) -Returns message attributes. - id (Integer) message ID - attributes (JSONArray) (optional) \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/sms/smsGetMessageCount.txt b/qsl4a-src/androidhelper/doc/sms/smsGetMessageCount.txt deleted file mode 100644 index a7b41f6..0000000 --- a/qsl4a-src/androidhelper/doc/sms/smsGetMessageCount.txt +++ /dev/null @@ -1,4 +0,0 @@ -smsGetMessageCount(unreadOnly,folder="inbox") -Returns the number of messages. - unreadOnly (Boolean) - folder (String) (default=inbox) \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/sms/smsGetMessageIds.txt b/qsl4a-src/androidhelper/doc/sms/smsGetMessageIds.txt deleted file mode 100644 index fe27419..0000000 --- a/qsl4a-src/androidhelper/doc/sms/smsGetMessageIds.txt +++ /dev/null @@ -1,4 +0,0 @@ -smsGetMessageIds(unreadOnly,folder="inbox") -Returns a List of all message IDs. - unreadOnly (Boolean) - folder (String) (default=inbox) \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/sms/smsGetMessages.txt b/qsl4a-src/androidhelper/doc/sms/smsGetMessages.txt deleted file mode 100644 index 84417fb..0000000 --- a/qsl4a-src/androidhelper/doc/sms/smsGetMessages.txt +++ /dev/null @@ -1,6 +0,0 @@ -smsGetMessages(unreadOnly,folder="inbox",attributes=None) -Returns a List of all messages. - unreadOnly (Boolean) - folder (String) (default=inbox) - attributes (JSONArray) (optional) -returns: (List) a List of messages as Maps \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/sms/smsMarkMessageRead.txt b/qsl4a-src/androidhelper/doc/sms/smsMarkMessageRead.txt deleted file mode 100644 index e8f83e1..0000000 --- a/qsl4a-src/androidhelper/doc/sms/smsMarkMessageRead.txt +++ /dev/null @@ -1,5 +0,0 @@ -smsMarkMessageRead(ids,read) -Marks messages as read. - ids (JSONArray) List of message IDs to mark as read. - read (Boolean) -returns: (Integer) number of messages marked read \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/sms/smsSend.txt b/qsl4a-src/androidhelper/doc/sms/smsSend.txt deleted file mode 100644 index 6bd9b60..0000000 --- a/qsl4a-src/androidhelper/doc/sms/smsSend.txt +++ /dev/null @@ -1,4 +0,0 @@ -smsSend(destinationAddress,text) -Sends an SMS. You need start SMS permission first . - destinationAddress (String) typically a phone number - text (String) \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/tts/speechToText.txt b/qsl4a-src/androidhelper/doc/tts/speechToText.txt deleted file mode 100644 index 1ab181f..0000000 --- a/qsl4a-src/androidhelper/doc/tts/speechToText.txt +++ /dev/null @@ -1,6 +0,0 @@ -speechToText(RecordSecond = 10, AmrFile = None, Language = None) - RecordSecond ( Integer , Optional ) Recording duration , - AmrFile ( String , Optional ) Temporary stored amr file , - If RecordSecond is None , use only AmrFile to input speech content . - If AmrFile is None , use AmrFile = os.environ['TMP']+'/tmp.amr' . - If Language is None, use system language . diff --git a/qsl4a-src/androidhelper/doc/tts/textToSpeech.txt b/qsl4a-src/androidhelper/doc/tts/textToSpeech.txt deleted file mode 100644 index 2544184..0000000 --- a/qsl4a-src/androidhelper/doc/tts/textToSpeech.txt +++ /dev/null @@ -1,5 +0,0 @@ -textToSpeech(Text, AutoPlay = True, WavFile = None, VoiceName = None) - Text (String) the String to convert to speech , - AutoPlay (Boolean Optional) auto play the speech , - WavFile (String Optional) the wav file to store the speech, if it is None, means os.environ['TMP']+'/tmp.wav' , - VoiceName (String Optional) diff --git a/qsl4a-src/androidhelper/doc/tts/ttsIsSpeaking.txt b/qsl4a-src/androidhelper/doc/tts/ttsIsSpeaking.txt deleted file mode 100644 index 52962d7..0000000 --- a/qsl4a-src/androidhelper/doc/tts/ttsIsSpeaking.txt +++ /dev/null @@ -1,2 +0,0 @@ -ttsIsSpeaking() -Returns True if speech is currently in progress. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/tts/ttsSpeak.txt b/qsl4a-src/androidhelper/doc/tts/ttsSpeak.txt deleted file mode 100644 index 67fe4f2..0000000 --- a/qsl4a-src/androidhelper/doc/tts/ttsSpeak.txt +++ /dev/null @@ -1,5 +0,0 @@ -ttsSpeak(message,pitch=1,pitchRate=1) -Speaks the provided message via TTS. - message (String) - pitch (Float Optional) speech pitch - pitchRate (Float Optional) speech pitch rate \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/tts/ttsStop.txt b/qsl4a-src/androidhelper/doc/tts/ttsStop.txt deleted file mode 100644 index 9bcbbcb..0000000 --- a/qsl4a-src/androidhelper/doc/tts/ttsStop.txt +++ /dev/null @@ -1,2 +0,0 @@ -ttsStop() -Stop the speaking TTS . \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/usb/usbserialConnect.txt b/qsl4a-src/androidhelper/doc/usb/usbserialConnect.txt deleted file mode 100644 index 846633d..0000000 --- a/qsl4a-src/androidhelper/doc/usb/usbserialConnect.txt +++ /dev/null @@ -1,2 +0,0 @@ -usbserialConnect(hash = "", options ="") -Connect to a device with USB-Host. request the connection and exit. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/usb/usbserialDisconnect.txt b/qsl4a-src/androidhelper/doc/usb/usbserialDisconnect.txt deleted file mode 100644 index 0689926..0000000 --- a/qsl4a-src/androidhelper/doc/usb/usbserialDisconnect.txt +++ /dev/null @@ -1,2 +0,0 @@ -usbserialDisconnect(connID = "") -Disconnect all USB-device. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/usb/usbserialGetDeviceList.txt b/qsl4a-src/androidhelper/doc/usb/usbserialGetDeviceList.txt deleted file mode 100644 index e1abf9f..0000000 --- a/qsl4a-src/androidhelper/doc/usb/usbserialGetDeviceList.txt +++ /dev/null @@ -1,3 +0,0 @@ -usbserialGetDeviceList() -Returns USB devices reported by USB Host API., -returns Map of id and string information ',' separated \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/usb/usbserialRead.txt b/qsl4a-src/androidhelper/doc/usb/usbserialRead.txt deleted file mode 100644 index ae849e8..0000000 --- a/qsl4a-src/androidhelper/doc/usb/usbserialRead.txt +++ /dev/null @@ -1,2 +0,0 @@ -usbserialRead(connID = "", bufferSize = 4096) -Read up to bufferSize ASCII characters. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/usb/usbserialReadReady.txt b/qsl4a-src/androidhelper/doc/usb/usbserialReadReady.txt deleted file mode 100644 index 533130f..0000000 --- a/qsl4a-src/androidhelper/doc/usb/usbserialReadReady.txt +++ /dev/null @@ -1,2 +0,0 @@ -usbserialReadReady(connID = "") -Returns True if the next read is guaranteed not to block. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/usb/usbserialWrite.txt b/qsl4a-src/androidhelper/doc/usb/usbserialWrite.txt deleted file mode 100644 index 2a63de5..0000000 --- a/qsl4a-src/androidhelper/doc/usb/usbserialWrite.txt +++ /dev/null @@ -1,2 +0,0 @@ -usbserialWrite(ascii, connID = "") -Sends ASCII characters over the currently open USB Serial connection. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/wakeLock/wakeLockAcquireBright.txt b/qsl4a-src/androidhelper/doc/wakeLock/wakeLockAcquireBright.txt deleted file mode 100644 index 5ad8019..0000000 --- a/qsl4a-src/androidhelper/doc/wakeLock/wakeLockAcquireBright.txt +++ /dev/null @@ -1,2 +0,0 @@ -wakeLockAcquireBright() -Acquires a bright wake lock (CPU on, screen bright). \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/wakeLock/wakeLockAcquireDim.txt b/qsl4a-src/androidhelper/doc/wakeLock/wakeLockAcquireDim.txt deleted file mode 100644 index 33fa5bd..0000000 --- a/qsl4a-src/androidhelper/doc/wakeLock/wakeLockAcquireDim.txt +++ /dev/null @@ -1,2 +0,0 @@ -wakeLockAcquireDim() -Acquires a dim wake lock (CPU on, screen dim). \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/wakeLock/wakeLockAcquireFull.txt b/qsl4a-src/androidhelper/doc/wakeLock/wakeLockAcquireFull.txt deleted file mode 100644 index 013b3ae..0000000 --- a/qsl4a-src/androidhelper/doc/wakeLock/wakeLockAcquireFull.txt +++ /dev/null @@ -1,2 +0,0 @@ -wakeLockAcquireFull() -Acquires a full wake lock (CPU on, screen bright, keyboard bright). \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/wakeLock/wakeLockAcquirePartial.txt b/qsl4a-src/androidhelper/doc/wakeLock/wakeLockAcquirePartial.txt deleted file mode 100644 index c896805..0000000 --- a/qsl4a-src/androidhelper/doc/wakeLock/wakeLockAcquirePartial.txt +++ /dev/null @@ -1,2 +0,0 @@ -wakeLockAcquirePartial() -Acquires a partial wake lock (CPU on). \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/wakeLock/wakeLockRelease.txt b/qsl4a-src/androidhelper/doc/wakeLock/wakeLockRelease.txt deleted file mode 100644 index 8e50cf9..0000000 --- a/qsl4a-src/androidhelper/doc/wakeLock/wakeLockRelease.txt +++ /dev/null @@ -1,2 +0,0 @@ -wakeLockRelease() -Releases the wake lock. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/webSpeed/getConnectedInfo.txt b/qsl4a-src/androidhelper/doc/webSpeed/getConnectedInfo.txt deleted file mode 100644 index d28d60e..0000000 --- a/qsl4a-src/androidhelper/doc/webSpeed/getConnectedInfo.txt +++ /dev/null @@ -1,3 +0,0 @@ -getConnectedInfo() - return Dict in List of IpAddress,MacAddress,Device . - Only support Android <= 9 . diff --git a/qsl4a-src/androidhelper/doc/webSpeed/getDhcpInfo.txt b/qsl4a-src/androidhelper/doc/webSpeed/getDhcpInfo.txt deleted file mode 100644 index 49544d6..0000000 --- a/qsl4a-src/androidhelper/doc/webSpeed/getDhcpInfo.txt +++ /dev/null @@ -1,4 +0,0 @@ -getDhcpInfo(ipConvertToString=True) - return DHCP Infomation Dict . - If ipConvertToString is True, IP address is like "10.0.3.193", - If ipConvertToString is False, IP address is like 167773121. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/webSpeed/getInternetInterfaceAddress.txt b/qsl4a-src/androidhelper/doc/webSpeed/getInternetInterfaceAddress.txt deleted file mode 100644 index bd0983a..0000000 --- a/qsl4a-src/androidhelper/doc/webSpeed/getInternetInterfaceAddress.txt +++ /dev/null @@ -1,2 +0,0 @@ -getInternetInterfaceAddress() -Returns a dict of InterfaceName and InetAddressList . \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/webSpeed/getMobileRxBytes.txt b/qsl4a-src/androidhelper/doc/webSpeed/getMobileRxBytes.txt deleted file mode 100644 index f4eb7d0..0000000 --- a/qsl4a-src/androidhelper/doc/webSpeed/getMobileRxBytes.txt +++ /dev/null @@ -1,2 +0,0 @@ -getMobileRxBytes() -Return a map of Mobile Received Bytes since startup and Startup Time Millis . diff --git a/qsl4a-src/androidhelper/doc/webSpeed/getMobileTxBytes.txt b/qsl4a-src/androidhelper/doc/webSpeed/getMobileTxBytes.txt deleted file mode 100644 index c140ace..0000000 --- a/qsl4a-src/androidhelper/doc/webSpeed/getMobileTxBytes.txt +++ /dev/null @@ -1,2 +0,0 @@ -getMobileTxBytes() -Return a map of Mobile Transmit Bytes since startup and Startup Time Millis . diff --git a/qsl4a-src/androidhelper/doc/webSpeed/getQPyRxBytes.txt b/qsl4a-src/androidhelper/doc/webSpeed/getQPyRxBytes.txt deleted file mode 100644 index ef820f7..0000000 --- a/qsl4a-src/androidhelper/doc/webSpeed/getQPyRxBytes.txt +++ /dev/null @@ -1,2 +0,0 @@ -getQPyRxBytes() -Return a map of QPython Received Bytes since startup and Startup Time Millis . diff --git a/qsl4a-src/androidhelper/doc/webSpeed/getQPyTxBytes.txt b/qsl4a-src/androidhelper/doc/webSpeed/getQPyTxBytes.txt deleted file mode 100644 index 3b947f4..0000000 --- a/qsl4a-src/androidhelper/doc/webSpeed/getQPyTxBytes.txt +++ /dev/null @@ -1,2 +0,0 @@ -getQPyTxBytes() -Return a map of QPython Transmit Bytes since startup and Startup Time Millis . diff --git a/qsl4a-src/androidhelper/doc/webSpeed/getTotalRxBytes.txt b/qsl4a-src/androidhelper/doc/webSpeed/getTotalRxBytes.txt deleted file mode 100644 index ddba26f..0000000 --- a/qsl4a-src/androidhelper/doc/webSpeed/getTotalRxBytes.txt +++ /dev/null @@ -1,2 +0,0 @@ -getTotalRxBytes() -Return a map of Total Received Bytes since startup and Startup Time Millis . diff --git a/qsl4a-src/androidhelper/doc/webSpeed/getTotalTxBytes.txt b/qsl4a-src/androidhelper/doc/webSpeed/getTotalTxBytes.txt deleted file mode 100644 index 435ea44..0000000 --- a/qsl4a-src/androidhelper/doc/webSpeed/getTotalTxBytes.txt +++ /dev/null @@ -1,2 +0,0 @@ -getTotalTxBytes() -Return a map of Total Transmit Bytes since startup and Startup Time Millis . diff --git a/qsl4a-src/androidhelper/doc/webSpeed/getTrafficStats.txt b/qsl4a-src/androidhelper/doc/webSpeed/getTrafficStats.txt deleted file mode 100644 index 4ac4dd4..0000000 --- a/qsl4a-src/androidhelper/doc/webSpeed/getTrafficStats.txt +++ /dev/null @@ -1,3 +0,0 @@ -getTrafficStats(flags=7) -flags (Integer Optional) ALL(7,Default) = TOTAL(1) + MOBILE(2) + QPYTHON(4) -Return a map of Rx/Tx Bytes since startup and Startup Time millis . \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/wifi/checkWifiState.txt b/qsl4a-src/androidhelper/doc/wifi/checkWifiState.txt deleted file mode 100644 index b407750..0000000 --- a/qsl4a-src/androidhelper/doc/wifi/checkWifiState.txt +++ /dev/null @@ -1,3 +0,0 @@ -checkWifiState() -Checks Wifi state. -returns: (Boolean) True if Wifi is enabled. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/wifi/toggleWifiState.txt b/qsl4a-src/androidhelper/doc/wifi/toggleWifiState.txt deleted file mode 100644 index 35198d4..0000000 --- a/qsl4a-src/androidhelper/doc/wifi/toggleWifiState.txt +++ /dev/null @@ -1,4 +0,0 @@ -toggleWifiState(enabled=None) -Toggle Wifi on and off. - enabled (Boolean) (optional) -returns: (Boolean) True if Wifi is enabled. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/wifi/wifiDisconnect.txt b/qsl4a-src/androidhelper/doc/wifi/wifiDisconnect.txt deleted file mode 100644 index 788aaad..0000000 --- a/qsl4a-src/androidhelper/doc/wifi/wifiDisconnect.txt +++ /dev/null @@ -1,3 +0,0 @@ -wifiDisconnect() -Disconnects from the currently active access point. -returns: (Boolean) True if the operation succeeded. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/wifi/wifiGetApState.txt b/qsl4a-src/androidhelper/doc/wifi/wifiGetApState.txt deleted file mode 100644 index a56335b..0000000 --- a/qsl4a-src/androidhelper/doc/wifi/wifiGetApState.txt +++ /dev/null @@ -1,2 +0,0 @@ -wifiGetApState() - return "disabling","disabled","enabling","enabled","failed" \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/wifi/wifiGetConnectionInfo.txt b/qsl4a-src/androidhelper/doc/wifi/wifiGetConnectionInfo.txt deleted file mode 100644 index b9855e9..0000000 --- a/qsl4a-src/androidhelper/doc/wifi/wifiGetConnectionInfo.txt +++ /dev/null @@ -1,4 +0,0 @@ -wifiGetConnectionInfo(ipConvertToString=True) -Returns information about the currently active access point. - If ipConvertToString is True, IP address is like "10.0.3.193", - If ipConvertToString is False, IP address is like 167773121. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/wifi/wifiGetScanResults.txt b/qsl4a-src/androidhelper/doc/wifi/wifiGetScanResults.txt deleted file mode 100644 index 0b3f740..0000000 --- a/qsl4a-src/androidhelper/doc/wifi/wifiGetScanResults.txt +++ /dev/null @@ -1,2 +0,0 @@ -wifiGetScanResults() -Returns the list of access points found during the most recent Wifi scan. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/wifi/wifiLockAcquireFull.txt b/qsl4a-src/androidhelper/doc/wifi/wifiLockAcquireFull.txt deleted file mode 100644 index 6786b41..0000000 --- a/qsl4a-src/androidhelper/doc/wifi/wifiLockAcquireFull.txt +++ /dev/null @@ -1,2 +0,0 @@ -wifiLockAcquireFull() -Acquires a full Wifi lock. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/wifi/wifiLockAcquireScanOnly.txt b/qsl4a-src/androidhelper/doc/wifi/wifiLockAcquireScanOnly.txt deleted file mode 100644 index 85b8fb2..0000000 --- a/qsl4a-src/androidhelper/doc/wifi/wifiLockAcquireScanOnly.txt +++ /dev/null @@ -1,2 +0,0 @@ -wifiLockAcquireScanOnly() -Acquires a scan only Wifi lock. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/wifi/wifiLockRelease.txt b/qsl4a-src/androidhelper/doc/wifi/wifiLockRelease.txt deleted file mode 100644 index d82cfc5..0000000 --- a/qsl4a-src/androidhelper/doc/wifi/wifiLockRelease.txt +++ /dev/null @@ -1,2 +0,0 @@ -wifiLockRelease() -Releases a previously acquired Wifi lock. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/wifi/wifiReassociate.txt b/qsl4a-src/androidhelper/doc/wifi/wifiReassociate.txt deleted file mode 100644 index 64a85a2..0000000 --- a/qsl4a-src/androidhelper/doc/wifi/wifiReassociate.txt +++ /dev/null @@ -1,3 +0,0 @@ -wifiReassociate() -Reassociates with the currently active access point. -returns: (Boolean) True if the operation succeeded. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/wifi/wifiReconnect.txt b/qsl4a-src/androidhelper/doc/wifi/wifiReconnect.txt deleted file mode 100644 index 81cf0ca..0000000 --- a/qsl4a-src/androidhelper/doc/wifi/wifiReconnect.txt +++ /dev/null @@ -1,3 +0,0 @@ -wifiReconnect() -Reconnects to the currently active access point. -returns: (Boolean) True if the operation succeeded. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/doc/wifi/wifiStartScan.txt b/qsl4a-src/androidhelper/doc/wifi/wifiStartScan.txt deleted file mode 100644 index d5e4dad..0000000 --- a/qsl4a-src/androidhelper/doc/wifi/wifiStartScan.txt +++ /dev/null @@ -1,3 +0,0 @@ -wifiStartScan() -Starts a scan for Wifi access points. -returns: (Boolean) True if the scan was initiated successfully. \ No newline at end of file diff --git a/qsl4a-src/androidhelper/documentFile.py b/qsl4a-src/androidhelper/documentFile.py deleted file mode 100644 index e3ed38f..0000000 --- a/qsl4a-src/androidhelper/documentFile.py +++ /dev/null @@ -1,175 +0,0 @@ -from base64 import b64encode as encode, b64decode as decode -from .sl4a import Result -from .__init__ import Android -import os -sdcard = os.environ['EXTERNAL_STORAGE'] - -class documentFile: - def __init__( this, sup ): - for i in dir(this): - if i[0] != '_': - exec(f'sup.documentFile{i}=this.{i}') - this._rpc = sup._rpc - this._write = sup._write - this._flush = sup._flush - this._readline = sup._readline - this._sup = sup - sup.documentFileShowOpen = sup.documentTreeShowOpen = this._TreeShowOpen - sup.documentFileMoveTo = this.RenameTo - sup.documentFileMkdirs = this.Mkdir - sup.documentFileWriteTo = this.OutputStream - sup.documentFileReadFrom = this.InputStream - - def Mkdir( self, Dir ): - ''' -documentFileMkdir( Dir ) - Make New Directory(s) - documentFileMkdirs is the same as documentFileMkdir . - Return True if Success . -''' - return self._rpc("documentFileMkdir", Dir) - - def Delete( self, FileOrTree ): - ''' -documentFileDelete( FileOrTree ) - Return True if Success . -''' - return self._rpc("documentFileDelete", FileOrTree) - - def RenameTo( self, Src, Dest ): - ''' -documentFileRenameTo( Src, Dest ) - documentFileMoveTo is the same as documentFileRenameTo - Move or Rename Tree or File . - Return True if Success . -''' - return self._rpc("documentFileRenameTo", Src, Dest ) - - def Copy( self, SrcFileOrTree, DestFileOrTree ): - ''' -documentFileCopy( SrcFileOrTree, DestFileOrTree ) - Return None . -''' - return self._rpc("documentFileCopy", SrcFileOrTree, DestFileOrTree ) - - def ListFiles( self, Folder ): - ''' -documentFileListFiles( Folder ) - Return List of Files of the Folder . -''' - return self._rpc("documentFileListFiles", Folder ) - - def InputStream( self, srcFile, EncodingFormat = "", skip = None, length = None ): - ''' -documentFileInputStream( srcFile, EncodingFormat = "", skip = None, length = None ) -documentFileReadFrom is the same as documentFileInputStream . -If EncodingFormat is omitted ( empty default ) , this function will return Bytes ; -If EncodingFormat is Base64 , this function will return Base64 String ; -If EncodingFormat is UTF-8 or GBK etc , this function will return Normal String . -If skip is None default, skip == 0 (start of srcFile) , otherwise skip bytes from srcFile's head, -If length is None default, length means rest of srcFile, read length bytes from skip place . -''' - if EncodingFormat: - if EncodingFormat.upper()=='BASE64': - EncodingFormat='' - return self._rpc("documentFileInputStream", srcFile, EncodingFormat, skip, length) - else: - r=self._rpc("documentFileInputStream", srcFile, '', skip, length) - return Result(r.id,decode(r.result),r.error) - - def OutputStream( self, destFile, src, EncodingFormat = "", append = None ): - ''' -documentFileOutputStream( destFile, src, EncodingFormat = "", append = None ) -documentFileWriteTo is the same as documentFileOutputStream . -If EncodingFormat is omitted (empty default) , src will be Bytes ; -If EncodingFormat is Base64 , src will be Base64 String ; -If EncodingFormat is UTF-8 or GBK etc , src will be Normal String ; -When a destFile exists, if append is True, append content to the end of the destFile, if append is False or None, overwrite the original destFile . -This Function always return None . -''' - if EncodingFormat: - if EncodingFormat.upper()=='BASE64': - EncodingFormat='' - return self._rpc("documentFileOutputStream", destFile, src, EncodingFormat, append) - else: - return self._rpc("documentFileOutputStream", destFile, encode( src ).decode(), '', append) - - def GetUri(self, path, isDirectory = None): - ''' -documentFileGetUri(path, isDirectory = None) -Return $URI according to the specific path . - path (String) - isDirectory (Boolean Optional) : - True - must be a directory , create an empty folder if not exist ; - False - must not be a directory , create an empty file if not exist ; - None - don't know if it is a directory , create nothing and return None if not exist . -''' - return self._rpc("documentFileGetUri", path, isDirectory ) - - def IsDirectory(self, path): - ''' -documentFileIsDirectory(path) -Return a path is a directory , - if path not exists or not available , return None . - path (String) -''' - return self._rpc("documentFileIsDirectory", path ) - - def IsFile(self, path): - ''' -documentFileIsFile(path) -Return a path is a file , - if path not exists or not available , return None . - path (String) -''' - return self._rpc("documentFileIsFile", path ) - - def Exists(self, path): - ''' -documentFileExists(path) -Return a path is exists , - path (String) -''' - return self._rpc("documentFileExists", path ) - - def LastModified(self, path): - ''' -documentFileLastModified(path) -Return a path's Last Modified time, - if path not exists or not available , return 0 . - path (String) -''' - return self._rpc("documentFileLastModified", path ) - - def Length(self, path): - ''' -documentFileLength(path) -Return a path's file size, - if path not exists or not available , return 0 . - path (String) -''' - return self._rpc("documentFileLength", path ) - - def GetStat(self, path): - ''' -documentFileGetStat(path) -Return a dict of file length , last modified and file can read/write , - if path not exists or not available , return None . - path (String) -''' - return self._rpc("documentFileGetStat", path ) - - def _TreeShowOpen( self, rootPath = sdcard ): - return self._rpc("documentTreeShowOpen", rootPath ) - -_i='"content://com.android.externalstorage.documents/"' -_d=documentFile.GetUri -_d.__doc__=_d.__doc__.replace('$URI',_i) -documentFile._TreeShowOpen.__doc__=Android.documentTreeShowOpen.__doc__.replace('/sdcard',sdcard).replace('$URI',_i) -_d='Use This Function to Read/Write Media Storage Device or "/sdcard/Android/" .'.replace('/sdcard',sdcard) -for _i in dir(documentFile): - if _i[0]!='_': - exec('documentFile.%s.__doc__+=_d'%_i) -del _d,_i - -#by 乘着船 2021-2023 \ No newline at end of file diff --git a/qsl4a-src/androidhelper/floatView/@.ini b/qsl4a-src/androidhelper/floatView/@.ini deleted file mode 100644 index bb163f3..0000000 --- a/qsl4a-src/androidhelper/floatView/@.ini +++ /dev/null @@ -1,13 +0,0 @@ -INDEX_NEW = -1 -FLAG_DEFAULT_TOUCHABLE = 40 -FLAG_DEFAULT_NOT_TOUCHABLE = 24 -FLAG_NOT_TOUCH_MODAL = 32 -FLAG_NOT_FOCUSABLE = 8 -FLAG_NOT_TOUCHABLE = 16 -TEXT_ALIGNMENT_INHERIT = 0 -TEXT_ALIGNMENT_GRAVITY = 1 -TEXT_ALIGNMENT_TEXT_START = 2 -TEXT_ALIGNMENT_TEXT_END = 3 -TEXT_ALIGNMENT_CENTER = 4 -TEXT_ALIGNMENT_VIEW_START = 5 -TEXT_ALIGNMENT_VIEW_END = 6 \ No newline at end of file diff --git a/qsl4a-src/androidhelper/floatView/@.txt b/qsl4a-src/androidhelper/floatView/@.txt deleted file mode 100644 index 86c6be5..0000000 --- a/qsl4a-src/androidhelper/floatView/@.txt +++ /dev/null @@ -1,33 +0,0 @@ -floatView(Args=None) - Show Float View . -# FloatView Args is a dict which default (empty) value is : -{ - 'index':-1, - # if index is omitted ( i.e. floatView.INDEX_NEW = -1 ) , create a new Float View, - # if index > 0, modify an existed Float View with index . - 'text':"drag move\nlong click close", - # FloatView Drag Move , Long Click Close - 'html':, - # if has text, html will be omitted, if text is omitted and html has content, FloatView will use html - 'width':300, - 'height':150, - 'x':0, - 'y':0, - # original point (x=y=0) is the center of screen - # x,y,width,height can omitted means "last state" - 'backColor':'7f7f7f7f', - 'textColor':'ff000000', - # backColor and textColor can omitted means "last state" - 'textSize':10, - 'textAlign':0, - # floatView.TEXT_ALIGNMENT_INHERIT = 0 - # textSize and textAlign can omitted means "last state" - 'script':, # script path - # after Long Click Close the FloatView, run script with script arg, if script is omitted, do nothing after Long Click Close the FloatView - 'arg':, # script arg - # script arg is use with script - 'clickRemove':True, - 'flag':40 - # floatView.FLAG_DEFAULT_TOUCHABLE = 40 -} - Return Float View current chain list length (not the number of Float Views) . \ No newline at end of file diff --git a/qsl4a-src/androidhelper/floatView/@Count.txt b/qsl4a-src/androidhelper/floatView/@Count.txt deleted file mode 100644 index 81525fc..0000000 --- a/qsl4a-src/androidhelper/floatView/@Count.txt +++ /dev/null @@ -1,3 +0,0 @@ -floatViewCount() -Count Float View number , - Returns an integer . \ No newline at end of file diff --git a/qsl4a-src/androidhelper/floatView/@Remove.ini b/qsl4a-src/androidhelper/floatView/@Remove.ini deleted file mode 100644 index d618cd8..0000000 --- a/qsl4a-src/androidhelper/floatView/@Remove.ini +++ /dev/null @@ -1 +0,0 @@ -ALL = -1 diff --git a/qsl4a-src/androidhelper/floatView/@Remove.txt b/qsl4a-src/androidhelper/floatView/@Remove.txt deleted file mode 100644 index 00729df..0000000 --- a/qsl4a-src/androidhelper/floatView/@Remove.txt +++ /dev/null @@ -1,4 +0,0 @@ -floatViewRemove(index = -1) -Remove Float View with index, - index (Integer Optional) If index is omitted ( i.e. floatViewRemove.ALL = -1 ) , Remove All Float Views , - Return the number of removed Float Views . \ No newline at end of file diff --git a/qsl4a-src/androidhelper/floatView/@Result.ini b/qsl4a-src/androidhelper/floatView/@Result.ini deleted file mode 100644 index fb81b3b..0000000 --- a/qsl4a-src/androidhelper/floatView/@Result.ini +++ /dev/null @@ -1 +0,0 @@ -LAST = -1 diff --git a/qsl4a-src/androidhelper/floatView/@Result.txt b/qsl4a-src/androidhelper/floatView/@Result.txt deleted file mode 100644 index 12525d0..0000000 --- a/qsl4a-src/androidhelper/floatView/@Result.txt +++ /dev/null @@ -1,4 +0,0 @@ -floatViewResult(index = -1) -Return Float View Result with index: - x, y, time (miliseconds) , operation, index . -index (Integer Optional) if index is omitted ( i.e. floatViewResult.LAST = -1 ) , return the Result of the last Float View . \ No newline at end of file diff --git a/qsl4a-src/androidhelper/floatView/Code.py b/qsl4a-src/androidhelper/floatView/Code.py deleted file mode 100644 index 7a72441..0000000 --- a/qsl4a-src/androidhelper/floatView/Code.py +++ /dev/null @@ -1,8 +0,0 @@ -def @(self,Args=None): - return self._rpc("@",Args) -def @Result(self,index=-1): - return self._rpc("@Result",index) -def @Remove(self,index=-1): - return self._rpc("@Remove",index) -def @Count(self): - return self._rpc("@Count") \ No newline at end of file diff --git a/qsl4a-src/androidhelper/floatView/Example.txt b/qsl4a-src/androidhelper/floatView/Example.txt deleted file mode 100644 index 920cc64..0000000 --- a/qsl4a-src/androidhelper/floatView/Example.txt +++ /dev/null @@ -1,25 +0,0 @@ -===== ===== ===== ===== -Float View Functions Example : - ->>> from androidhelper import * ->>> droid = Android() ->>> droid.floatView({'index':0,'text':'Hello World','width':400,'height':300,'x':-300,'y':-400,'backColor':'ff0000','textColor':'0000ff','textSize':16,'textAlign':droid.floatView.TEXT_ALIGNMENT_CENTER}) -Result(id=1, result=1, error=None) ->>> droid.floatViewCount() -Result(id=2, result=1, error=None) ->>> droid.floatViewResult() -Result(id=3, result={'x': -300, 'y': -400, 'time': 1772600484698, 'operation': 'initial', 'index': 0}, error=None) ->>> droid.floatView({'index':0,'text':'QPython FloatView','width':300,'height':400,'x':400,'y':300}) #Then manually move FloatView to a different position -Result(id=4, result=1, error=None) ->>> droid.floatViewResult() -Result(id=5, result={'x': -13, 'y': -580, 'time': 1772600575836, 'operation': 'move', 'index': 0}, error=None) ->>> droid.floatViewRemove(0) -Result(id=6, result=1, error=None) ->>> droid.floatViewResult() -Result(id=7, result={'removed': True, 'x': -13, 'y': -580, 'time': 1772600575836, 'operation': 'move', 'index': 0}, error=None) ->>> droid.floatViewRemove() -Result(id=8, result=0, error=None) ->>> droid.floatViewResult() -java.lang.Exception: Float View Index Out of Range . -Result(id=9, result=None, error='java.lang.Exception: Float View Index Out of Range .') -===== ===== ===== ===== \ No newline at end of file diff --git a/qsl4a-src/androidhelper/floatView/__init__.py b/qsl4a-src/androidhelper/floatView/__init__.py deleted file mode 100644 index 96cf27c..0000000 --- a/qsl4a-src/androidhelper/floatView/__init__.py +++ /dev/null @@ -1,21 +0,0 @@ -_k='floatView' -_c=__file__[:__file__.rfind('/')+1]+_k+'/' -_f=open(_c+'Code.py').read().replace('@',_k) -exec(_f.replace('@',_k)) -_e=open(_c+'Example.txt').read() -for _i in tuple(locals()): - if _i[:9]==_k: - _f=_c+'@'+_i[9:] - try: - _g=open(_f+'.ini').read().splitlines() - except: - _g=() - for _a in range(len(_g)): - _g[_a]=_i+'.'+_g[_a] - exec(_g[_a]) - _g='\n'.join(_g) - _f=open(_f+'.txt').read() - _f=(_f,_e,_g) - _f="\n\n".join(_f) - exec(f'{_i}.__doc__=_f') -del _a,_c,_e,_f,_g,_i,_k \ No newline at end of file diff --git a/qsl4a-src/androidhelper/fun/add.py b/qsl4a-src/androidhelper/fun/add.py deleted file mode 100644 index e7bc881..0000000 --- a/qsl4a-src/androidhelper/fun/add.py +++ /dev/null @@ -1,172 +0,0 @@ -#以下 SL4A函数 由 乘着船 添加 - -def videoPlay(self, path, wait=True): - return self._rpc("videoPlay",path,wait) - -def openFile(self, path, type=None, wait=True): - return self._rpc("openFile",path,type,wait) - -def sendFile(self, path, type=None, extras=None, wait=True): - return self._rpc("sendFile",path,type,extras,wait) - -def getApplicationInfo(self, packageName = None): - return self._rpc("getApplicationInfo",packageName) - -def recorderStartScreenRecord(self, path=None, audio=1, targetPixels=None, frameRate=30, bitRate=None, rotation=False, autoStart=True): - return self._rpc("recorderStartScreenRecord",path,audio,targetPixels,frameRate,bitRate,rotation,autoStart) - -def recorderPause(self): - return self._rpc("recorderPause") - -def recorderResume(self): - return self._rpc("recorderResume") - -def recorderStart(self): - return self._rpc("recorderStart") - -def imageReaderGetScreenShot(self, path = None, delayMilliSec = 1000): - return self._rpc("imageReaderGetScreenShot",path,delayMilliSec) - -def getMeid(self, slotIndex = None): - return self._rpc("getMeid",slotIndex) - -def getImei(self, slotIndex = None): - return self._rpc("getImei",slotIndex) - -def takePicture(self, path = None): - return self._rpc("takePicture",path) - -def takeVideo(self, path = None, quality = 1): - return self._rpc("takeVideo",path,quality) - -def recordAudio(self): - return self._rpc("recordAudio") - -def backgroundProtect(self, enabled = True): - return self._rpc("backgroundProtect", enabled) - -def recorderSoundVolumeDetect(self, interval = 100): - return self._rpc('recorderSoundVolumeDetect',interval) - -def recorderSoundVolumeGetDb(self): - return self._rpc('recorderSoundVolumeGetDb') - -def getLastLog(self,logFileName='last.log'): - return self._rpc("getLastLog",logFileName) - -def getLocale(self): - return self._rpc("getLocale") - -def getSysInfo(self): - return self._rpc('getSysInfo') - -def getAndroidID(self): - return self._rpc('getAndroidID') - -def getScreenInfo(self): - return self._rpc('getScreenInfo') - -def getMemoryInfo(self): - return self._rpc('getMemoryInfo') - -def createScriptShortCut(self,scriptPath,label=None,iconPath=None,scriptArg=None): - return self._rpc('createScriptShortCut',scriptPath,label,iconPath,scriptArg) - -def wifiGetApState(self): - return self._rpc('wifiGetApState') - -def getConnectedInfo(self): - return self._rpc('getConnectedInfo') - -def getDhcpInfo(self,ipConvertToString=True): - return self._rpc('getDhcpInfo',ipConvertToString) - -def getInstalledPackages(self, flag = 4): - return self._rpc("getInstalledPackages",flag) - -def getInternetInterfaceAddress(self): - return self._rpc("getInternetInterfaceAddress") - -def getTelephoneSignalStrengthDetail(self): - return self._rpc("getTelephoneSignalStrengthDetail") - -def getTelephoneSignalStrengthLevel(self): - return self._rpc("getTelephoneSignalStrengthLevel") - -def scanBarcodeFromImage(self, path, compressRatio = 0, x = 0, y = 0, width = 0, height = 0): - return self._rpc("scanBarcodeFromImage",path, compressRatio, x, y, width, height) - -def getHarmonyOsInformation(self): - return self._rpc("getHarmonyOsInformation") - -def isExternalStorageManager(self): - return self._rpc("isExternalStorageManager") - -def cameraCapturePicture(self, targetPath = None, cameraId = 0, useAutoFocus = True): - return self._rpc("cameraCapturePicture",targetPath, cameraId, useAutoFocus) - -def cameraSetTorchMode(self, enabled): - return self._rpc("cameraSetTorchMode",enabled) - -def recorderCaptureVideo(self, targetPath = None, duration = 10, cameraId = 0, quality = 8): - return self._rpc("recorderCaptureVideo",targetPath, duration, cameraId, quality) - -def getAllCellsLocation(self): - return self._rpc("getAllCellsLocation") - -def elapsedRealtimeNanos(self): - return self._rpc("elapsedRealtimeNanos") - -def sharedVariableSet(self, key, value): - return self._rpc("sharedVariableSet", key, value) - -def sharedVariableGet(self, key): - return self._rpc("sharedVariableGet", key) - -def sharedVariableRemove(self, key): - return self._rpc("sharedVariableRemove", key) - -def htmlPictureSetSize(self, widthFixed = None, heightFixed = None, widthRatio = None, heightRatio = None): - return self._rpc("htmlPictureSetSize", widthFixed, heightFixed, widthRatio, heightRatio) - -def htmlPictureGetSize(self): - return self._rpc("htmlPictureGetSize") - -def getFileStat(self, path): - return self._rpc("getFileStat", path) - -def getTrafficStats(self,flags=7): - return self._rpc("getTrafficStats",flags) - -def readGnssStatus(self): - return self._rpc("readGnssStatus") - -def prefRemoveValue(self,key,filename=None): - return self._rpc("prefRemoveValue",key,filename) - -def send(self,uri,type=None,extras=None,wait=True): - return self._rpc("send",uri,type,extras,wait) - -def sendText(self,text,extras=None,wait=True): - return self._rpc("sendText",text,extras,wait) - -def showScreenLock(self): - return self._rpc("showScreenLock") - -def checkPermissions(self): - return self._rpc('checkPermissions') - -def requestPermissions(self, permissions = None): - return self._rpc('requestPermissions', permissions) - -def ttsStop(self): - return self._rpc("ttsStop") - -def getSdCardPaths(self): - return self._rpc("getSdCardPaths") - -def imageCompress(self,srcPath,destPath,targetByteSize=0,targetWidth=0,targetHeight=0): - return self._rpc("imageCompress",srcPath,destPath,targetByteSize,targetWidth,targetHeight) - -def editorOpen(self, path): - return self._rpc("editorOpen", path) \ No newline at end of file diff --git a/qsl4a-src/androidhelper/fun/dialog.py b/qsl4a-src/androidhelper/fun/dialog.py deleted file mode 100644 index 26a42c5..0000000 --- a/qsl4a-src/androidhelper/fun/dialog.py +++ /dev/null @@ -1,83 +0,0 @@ -def dialogShowAlert( self, title = "Alert", message = "The message of the alert .", positiveButtonText = "OK", negativeButtonText = None, neutralButtonText = None, messageIsHtml = False ): - return self._rpc("dialogShowAlert", title, message, positiveButtonText, negativeButtonText, neutralButtonText, messageIsHtml) - -def dialogShowSimpleChoice(self, title = "Alert", message = "The message of the alert .", items = None, positiveButtonText = "OK", negativeButtonText = None, neutralButtonText = None, messageIsHtml = False ): - return self._rpc("dialogShowSimpleChoice", title, message, items, positiveButtonText, negativeButtonText, neutralButtonText, messageIsHtml) - -def dialogShowSingleChoice(self, title = "Alert",message = "The message of the alert .", items = None, selected = -1, positiveButtonText = "OK", negativeButtonText = None, neutralButtonText = None, messageIsHtml = False ): - return self._rpc("dialogShowSingleChoice", title, message, items, selected, positiveButtonText, negativeButtonText, neutralButtonText, messageIsHtml) - -def dialogShowMultiChoice(self, title = "Alert", message = "The message of the alert .", items = None, selected = None, positiveButtonText = "OK", negativeButtonText = None, neutralButtonText = None, messageIsHtml = False ): - return self._rpc("dialogShowMultiChoice", title, message, items, selected, positiveButtonText, negativeButtonText, neutralButtonText, messageIsHtml) - -def dialogSetProgressMessage(self,message): - return self._rpc("dialogSetProgressMessage",message) - -def dialogSetMessageIsHtml(self, messageIsHtml = True): - return self._rpc("dialogSetMessageIsHtml", messageIsHtml) - -def dialogGetInput(self,title="Value",message="Please enter value :",defaultText=None,messageIsHtml=False): - return self._rpc("dialogGetInput",title,message,defaultText,messageIsHtml) - -def dialogSetSingleChoiceItems(self,items,selected=-1): - return self._rpc("dialogSetSingleChoiceItems",items,selected) - -def dialogCreateAlert(self,title=None,message=None): - return self._rpc("dialogCreateAlert",title,message) - -def dialogCreateDatePicker(self,year=1970,month=1,day=1): - return self._rpc("dialogCreateDatePicker",year,month,day) - -def dialogCreateHorizontalProgress(self,title=None,message=None,maximum_progress=100): - return self._rpc("dialogCreateHorizontalProgress",title,message,maximum_progress) - -def dialogCreateInput(self,title="Value",message="Please enter value:",defaultText=None,inputType=None): - return self._rpc("dialogCreateInput",title,message,defaultText,inputType) - -def dialogCreatePassword(self,title="Password",message="Please enter password:"): - return self._rpc("dialogCreatePassword",title,message) - -def dialogCreateSeekBar(self,starting_value=50,maximum_value=100,title="",message=""): - return self._rpc("dialogCreateSeekBar",starting_value,maximum_value,title,message) - -def dialogCreateSpinnerProgress(self,title=None,message=None,maximum_progress=100): - return self._rpc("dialogCreateSpinnerProgress",title,message,maximum_progress) - -def dialogCreateTimePicker(self,hour=0,minute=0,is24hour=False): - return self._rpc("dialogCreateTimePicker",hour,minute,is24hour) - -def dialogDismiss(self): - return self._rpc("dialogDismiss") - -def dialogGetPassword(self,title="Password",message="Please enter password:"): - return self._rpc("dialogGetPassword",title,message) - -def dialogGetResponse(self): - return self._rpc("dialogGetResponse") - -def dialogGetSelectedItems(self): - return self._rpc("dialogGetSelectedItems") - -def dialogSetCurrentProgress(self,current): - return self._rpc("dialogSetCurrentProgress",current) - -def dialogSetItems(self,items): - return self._rpc("dialogSetItems",items) - -def dialogSetMaxProgress(self,max): - return self._rpc("dialogSetMaxProgress",max) - -def dialogSetMultiChoiceItems(self,items,selected=None): - return self._rpc("dialogSetMultiChoiceItems",items,selected) - -def dialogSetNegativeButtonText(self,text): - return self._rpc("dialogSetNegativeButtonText",text) - -def dialogSetNeutralButtonText(self,text): - return self._rpc("dialogSetNeutralButtonText",text) - -def dialogSetPositiveButtonText(self,text): - return self._rpc("dialogSetPositiveButtonText",text) - -def dialogShow(self): - return self._rpc("dialogShow") \ No newline at end of file diff --git a/qsl4a-src/androidhelper/fun/edit.py b/qsl4a-src/androidhelper/fun/edit.py deleted file mode 100644 index 3423d9c..0000000 --- a/qsl4a-src/androidhelper/fun/edit.py +++ /dev/null @@ -1,62 +0,0 @@ -#以下 SL4A函数 被 乘着船 修改 - -def makeToast(self,message,length=0,isHtml=False,backColor=None,textColor=None,textSize=0,textAlign=0): - return self._rpc("makeToast",message,length,isHtml,backColor,textColor,textSize,textAlign) - -def notify(self, title, message, uri=None, arg=None): - return self._rpc("notify",title,message,uri,arg) - -def getLaunchablePackages(self,needClassName=False): - return self._rpc("getLaunchablePackages",needClassName) - -def launch(self,classname=None,packagename=None,wait=True): - return self._rpc("launch",classname,packagename,wait) - -def view(self,uri,type=None,extras=None,wait=True): - return self._rpc("view",uri,type,extras,wait) - -def viewContacts(self,wait=True): - return self._rpc("viewContacts",wait) - -def viewMap(self,query,wait=True): - return self._rpc("viewMap",query,wait) - -def scanBarcode(self, title = None): - return self._rpc("scanBarcode",title) - -def getCellLocation(self): - return self._rpc("getCellLocation") - -def recorderStartMicrophone(self,targetPath=None): - return self._rpc("recorderStartMicrophone",targetPath) - -def smsSend(self,destinationAddress,text): - return self._rpc("smsSend",destinationAddress,text) - -def viewHtml(self,path,title=None,wait=True): - return self._rpc("viewHtml",path,title,wait) -webViewShow=viewHtml - -def executeQPy(self,scriptPath='',arg=None): - return self._rpc("executeQPy",scriptPath,arg) - -def startLocating(self,minUpdateTime=60000,minUpdateDistance=30,updateGnssStatus=False): - return self._rpc("startLocating",minUpdateTime,minUpdateDistance,updateGnssStatus) - -def setScreenBrightness(self,value=None): - return self._rpc("setScreenBrightness",value) - -def ttsSpeak(self,message,pitch=1,pitchRate=1): - return self._rpc("ttsSpeak",message,pitch,pitchRate) - -def sendEmail(self,to,subject,text,attachmentPath=None,extras=None,wait=True): - return self._rpc("sendEmail",to,subject,text,attachmentPath,extras,wait) - -def startActivity(self,action,uri=None,type=None,extras=None,wait=None,packagename=None,classname=None,flags=None): - return self._rpc("startActivity",action,uri,type,extras,wait,packagename,classname,flags) - -def startActivityForResult(self,action,uri=None,type=None,extras=None,packagename=None,classname=None,flags=None): - return self._rpc("startActivityForResult",action,uri,type,extras,packagename,classname,flags) - -def sendBroadcast(self,action,uri=None,type=None,extras=None,packagename=None,classname=None,flags=None): - return self._rpc("sendBroadcast",action,uri,type,extras,packagename,classname,flags) diff --git a/qsl4a-src/androidhelper/fun/ftp.py b/qsl4a-src/androidhelper/fun/ftp.py deleted file mode 100644 index a857c8f..0000000 --- a/qsl4a-src/androidhelper/fun/ftp.py +++ /dev/null @@ -1,19 +0,0 @@ -#以下 SL4A函数 由 乘着船 添加 - -def ftpGet(self): - return self._rpc("ftpGet") - -def ftpSet(self, port = None, rootDir = None, username = None, password = None): - return self._rpc("ftpSet", port, rootDir, username, password) - -def ftpStart(self): - return self._rpc("ftpStart") - -def ftpStop(self): - return self._rpc("ftpStop") - -def ftpIsRunning(self): - return self._rpc("ftpIsRunning") - -def ftpStatus(self): - return self._rpc("ftpStatus") \ No newline at end of file diff --git a/qsl4a-src/androidhelper/fun/full.py b/qsl4a-src/androidhelper/fun/full.py deleted file mode 100644 index 8bd603f..0000000 --- a/qsl4a-src/androidhelper/fun/full.py +++ /dev/null @@ -1,40 +0,0 @@ -#Full Screen Method - -def fullGetProperty(self,id,property): - return self._rpc("fullGetProperty",id,property) - -def fullGetProperties(self,ids,property): - return self._rpc("fullGetProperties",ids,property) - -def fullSetProperty(self,id,property,value): - return self._rpc("fullSetProperty",id,property,value) - -def fullSetProperties(self,ids,property,value): - return self._rpc("fullSetProperties",ids,property,value) - -def fullGetScreenShot(self, path = None): - return self._rpc("fullGetScreenShot",path) - -def fullSetList(self,id,list,isHtml=False,listType=0): - return self._rpc("fullSetList",id,list,isHtml,listType) - -def fullSetList2(self,id,list,intRes): - return self._rpc("fullSetList2",id,list,intRes) - -def fullSetListSelected(self,id,selected): - return self._rpc("fullSetListSelected",id,selected) - -def fullGetListSelected(self,id): - return self._rpc("fullGetListSelected",id) - -def fullShow(self,layout,title=None,theme=None): - return self._rpc("fullShow",layout,title,theme) - -def fullDismiss(self): - return self._rpc("fullDismiss") - -def fullQuery(self): - return self._rpc("fullQuery") - -def fullQueryDetail(self,id): - return self._rpc("fullQueryDetail",id) \ No newline at end of file diff --git a/qsl4a-src/androidhelper/fun/intent.py b/qsl4a-src/androidhelper/fun/intent.py deleted file mode 100644 index ed96e36..0000000 --- a/qsl4a-src/androidhelper/fun/intent.py +++ /dev/null @@ -1,17 +0,0 @@ -def makeIntent(self,action,uri=None,type=None,extras=None,categories=None,packagename=None,classname=None,flags=None): - return self._rpc("makeIntent",action,uri,type,self._intentExtras(extras),categories,packagename,classname,flags) - -def pathToUri(self,path,fileProvider=True,uriObject=False): - result=self._rpc("pathToUri",path,fileProvider) - if uriObject: - return sl4a.Result(result.id,self.Uri(result.result),result.error) - else: - return result - -def Uri(self,*s): - self.Uri=self.Intent._uri - return self.Uri(*s) - -def _intentExtras(self,*s): - self._intentExtras=self.Intent._intentExtras - return self._intentExtras(*s) \ No newline at end of file diff --git a/qsl4a-src/androidhelper/fun/origin.py b/qsl4a-src/androidhelper/fun/origin.py deleted file mode 100644 index 739e33d..0000000 --- a/qsl4a-src/androidhelper/fun/origin.py +++ /dev/null @@ -1,383 +0,0 @@ -def environment(self): - return self._rpc("environment") - -def getClipboard(self): - return self._rpc("getClipboard") - -def getConstants(self,classname): - return self._rpc("getConstants",classname) - -def getIntent(self): - return self._rpc("getIntent") - -def getPackageVersion(self,packageName): - return self._rpc("getPackageVersion",packageName) - -def getPackageVersionCode(self,packageName): - return self._rpc("getPackageVersionCode",packageName) - -def log(self,message): - return self._rpc("log",message) - -def sendBroadcastIntent(self,intent): - return self._rpc("sendBroadcastIntent",intent) - -def setClipboard(self,text): - return self._rpc("setClipboard",text) - -def startActivityForResultIntent(self,intent): - return self._rpc("startActivityForResultIntent",intent) - -def startActivityIntent(self,intent,wait=None): - return self._rpc("startActivityIntent",intent,wait) - -def vibrate(self,duration=300): - return self._rpc("vibrate",duration) - -def forceStopPackage(self,packageName): - return self._rpc("forceStopPackage",packageName) - -def getRunningPackages(self): - return self._rpc("getRunningPackages") - -def pick(self,uri): - return self._rpc("pick",uri) - -def search(self,query): - return self._rpc("search",query) - -def contactsGet(self,attributes=None): - return self._rpc("contactsGet",attributes) - -def contactsGetAttributes(self): - return self._rpc("contactsGetAttributes") - -def contactsGetById(self,id,attributes=None): - return self._rpc("contactsGetById",id,attributes) - -def contactsGetCount(self): - return self._rpc("contactsGetCount") - -def contactsGetIds(self): - return self._rpc("contactsGetIds") - -def pickContact(self): - return self._rpc("pickContact") - -def pickPhone(self): - return self._rpc("pickPhone") - -def queryAttributes(self,uri): - return self._rpc("queryAttributes",uri) - -def queryContent(self,uri,attributes=None,selection=None,selectionArgs=None,order=None): - return self._rpc("queryContent",uri,attributes,selection,selectionArgs,order) - -def eventClearBuffer(self): - return self._rpc("eventClearBuffer") - -def eventGetBrodcastCategories(self): - return self._rpc("eventGetBrodcastCategories") - -def eventPoll(self,number_of_events=1): - return self._rpc("eventPoll",number_of_events) - -def eventPost(self,name,data,enqueue=None): - return self._rpc("eventPost",name,data,enqueue) - -def eventRegisterForBroadcast(self,category,enqueue=True): - return self._rpc("eventRegisterForBroadcast",category,enqueue) - -def eventUnregisterForBroadcast(self,category): - return self._rpc("eventUnregisterForBroadcast",category) - -def eventWait(self,timeout=None): - return self._rpc("eventWait",timeout) - -def eventWaitFor(self,eventName,timeout=None): - return self._rpc("eventWaitFor",eventName,timeout) - -def receiveEvent(self): - return self._rpc("receiveEvent") - -def rpcPostEvent(self,name,data): - return self._rpc("rpcPostEvent",name,data) - -def startEventDispatcher(self,port=None): - return self._rpc("startEventDispatcher",port) - -def stopEventDispatcher(self): - return self._rpc("stopEventDispatcher") - -def waitForEvent(self,eventName,timeout=None): - return self._rpc("waitForEvent",eventName,timeout) - -def geocode(self,latitude,longitude,maxResults=1): - return self._rpc("geocode",latitude,longitude,maxResults) - -def getLastKnownLocation(self): - return self._rpc("getLastKnownLocation") - -def readLocation(self): - return self._rpc("readLocation") - -def stopLocating(self): - return self._rpc("stopLocating") - -def mediaIsPlaying(self,tag="default"): - return self._rpc("mediaIsPlaying",tag) - -def mediaPlay(self,url,tag="default",play=True): - return self._rpc("mediaPlay",url,tag,play) - -def mediaPlayClose(self,tag="default"): - return self._rpc("mediaPlayClose",tag) - -def mediaPlayInfo(self,tag="default"): - return self._rpc("mediaPlayInfo",tag) - -def mediaPlayList(self): - return self._rpc("mediaPlayList") - -def mediaPlayPause(self,tag="default"): - return self._rpc("mediaPlayPause",tag) - -def mediaPlaySeek(self,msec,tag="default"): - return self._rpc("mediaPlaySeek",msec,tag) - -def mediaPlaySetLooping(self,enabled=True,tag="default"): - return self._rpc("mediaPlaySetLooping",enabled,tag) - -def mediaPlayStart(self,tag="default"): - return self._rpc("mediaPlayStart",tag) - -def recorderStop(self): - return self._rpc("recorderStop") - -def checkNetworkRoaming(self): - return self._rpc("checkNetworkRoaming") - -def getDeviceSoftwareVersion(self): - return self._rpc("getDeviceSoftwareVersion") - -def getLine1Number(self): - return self._rpc("getLine1Number") - -def getAllCellInfo(self): - return self._rpc("getAllCellInfo") - -def getNetworkOperator(self): - return self._rpc("getNetworkOperator") - -def getNetworkOperatorName(self): - return self._rpc("getNetworkOperatorName") - -def getNetworkType(self): - return self._rpc("getNetworkType") - -def getPhoneType(self): - return self._rpc("getPhoneType") - -def getSimCountryIso(self): - return self._rpc("getSimCountryIso") - -def getSimOperator(self): - return self._rpc("getSimOperator") - -def getSimOperatorName(self): - return self._rpc("getSimOperatorName") - -def getSimSerialNumber(self): - return self._rpc("getSimSerialNumber") - -def getSimState(self): - return self._rpc("getSimState") - -def getSubscriberId(self): - return self._rpc("getSubscriberId") - -def getVoiceMailAlphaTag(self): - return self._rpc("getVoiceMailAlphaTag") - -def getVoiceMailNumber(self): - return self._rpc("getVoiceMailNumber") - -def phoneCall(self,uri): - return self._rpc("phoneCall",uri) - -def phoneCallNumber(self,phone_number): - return self._rpc("phoneCallNumber",phone_number) - -def phoneDial(self,uri): - return self._rpc("phoneDial",uri) - -def phoneDialNumber(self,phone_number): - return self._rpc("phoneDialNumber",phone_number) - -def readPhoneState(self): - return self._rpc("readPhoneState") - -def startTrackingPhoneState(self): - return self._rpc("startTrackingPhoneState") - -def stopTrackingPhoneState(self): - return self._rpc("stopTrackingPhoneState") - -def prefGetAll(self,filename=None): - return self._rpc("prefGetAll",filename) - -def prefGetValue(self,key,filename=None): - return self._rpc("prefGetValue",key,filename) - -def prefPutValue(self,key,value,filename=None): - return self._rpc("prefPutValue",key,value,filename) - -def stopSensing(self): - return self._rpc("stopSensing") - -def checkAirplaneMode(self): - return self._rpc("checkAirplaneMode") - -def checkRingerSilentMode(self): - return self._rpc("checkRingerSilentMode") - -def checkScreenOn(self): - return self._rpc("checkScreenOn") - -def getMaxMediaVolume(self): - return self._rpc("getMaxMediaVolume") - -def getMaxRingerVolume(self): - return self._rpc("getMaxRingerVolume") - -def getMediaVolume(self): - return self._rpc("getMediaVolume") - -def getRingerVolume(self): - return self._rpc("getRingerVolume") - -def getScreenBrightness(self): - return self._rpc("getScreenBrightness") - -def getScreenTimeout(self): - return self._rpc("getScreenTimeout") - -def getVibrateMode(self,ringer=None): - return self._rpc("getVibrateMode",ringer) - -def setMediaVolume(self,volume): - return self._rpc("setMediaVolume",volume) - -def setRingerVolume(self,volume): - return self._rpc("setRingerVolume",volume) - -def setScreenTimeout(self,value): - return self._rpc("setScreenTimeout",value) - -#def toggleAirplaneMode(self,enabled=None): -# return self._rpc("toggleAirplaneMode",enabled) - -def toggleRingerSilentMode(self,enabled=None): - return self._rpc("toggleRingerSilentMode",enabled) - -def toggleVibrateMode(self,enabled=None,ringer=None): - return self._rpc("toggleVibrateMode",enabled,ringer) - -def readSignalStrengths(self): - return self._rpc("readSignalStrengths") - -def startTrackingSignalStrengths(self): - return self._rpc("startTrackingSignalStrengths") - -def stopTrackingSignalStrengths(self): - return self._rpc("stopTrackingSignalStrengths") - -def smsDeleteMessage(self,id): - return self._rpc("smsDeleteMessage",id) - -def smsGetAttributes(self): - return self._rpc("smsGetAttributes") - -def smsGetMessageById(self,id,attributes=None): - return self._rpc("smsGetMessageById",id,attributes) - -def smsGetMessageCount(self,unreadOnly,folder="inbox"): - return self._rpc("smsGetMessageCount",unreadOnly,folder) - -def smsGetMessageIds(self,unreadOnly,folder="inbox"): - return self._rpc("smsGetMessageIds",unreadOnly,folder) - -def smsGetMessages(self,unreadOnly,folder="inbox",attributes=None): - return self._rpc("smsGetMessages",unreadOnly,folder,attributes) - -def smsMarkMessageRead(self,ids,read): - return self._rpc("smsMarkMessageRead",ids,read) - -def ttsIsSpeaking(self): - return self._rpc("ttsIsSpeaking") - -def generateDtmfTones(self,phoneNumber,toneDuration=100): - return self._rpc("generateDtmfTones",phoneNumber,toneDuration) - -def wakeLockAcquireBright(self): - return self._rpc("wakeLockAcquireBright") - -def wakeLockAcquireDim(self): - return self._rpc("wakeLockAcquireDim") - -def wakeLockAcquireFull(self): - return self._rpc("wakeLockAcquireFull") - -def wakeLockAcquirePartial(self): - return self._rpc("wakeLockAcquirePartial") - -def wakeLockRelease(self): - return self._rpc("wakeLockRelease") - -def cameraStartPreview(self,resolutionLevel=0,jpegQuality=20,filepath=None): - return self._rpc("cameraStartPreview",resolutionLevel,jpegQuality,filepath) - -def cameraStopPreview(self): - return self._rpc("cameraStopPreview") - -def webcamAdjustQuality(self,resolutionLevel=0,jpegQuality=20): - return self._rpc("webcamAdjustQuality",resolutionLevel,jpegQuality) - -def webcamStart(self,resolutionLevel=0,jpegQuality=20,port=0): - return self._rpc("webcamStart",resolutionLevel,jpegQuality,port) - -def webcamStop(self): - return self._rpc("webcamStop") - -def checkWifiState(self): - return self._rpc("checkWifiState") - -def toggleWifiState(self,enabled=None): - return self._rpc("toggleWifiState",enabled) - -def wifiDisconnect(self): - return self._rpc("wifiDisconnect") - -def wifiGetConnectionInfo(self): - return self._rpc("wifiGetConnectionInfo") - -def wifiGetScanResults(self): - return self._rpc("wifiGetScanResults") - -def wifiLockAcquireFull(self): - return self._rpc("wifiLockAcquireFull") - -def wifiLockAcquireScanOnly(self): - return self._rpc("wifiLockAcquireScanOnly") - -def wifiLockRelease(self): - return self._rpc("wifiLockRelease") - -def wifiReassociate(self): - return self._rpc("wifiReassociate") - -def wifiReconnect(self): - return self._rpc("wifiReconnect") - -def wifiStartScan(self): - return self._rpc("wifiStartScan") diff --git a/qsl4a-src/androidhelper/fun/special.py b/qsl4a-src/androidhelper/fun/special.py deleted file mode 100644 index 2f4f527..0000000 --- a/qsl4a-src/androidhelper/fun/special.py +++ /dev/null @@ -1,71 +0,0 @@ -#add Method - -def batteryStartMonitoring(self): - self._addMethod('battery') - return self.batteryStartMonitoring() - -def startSensingThreshold(self,sensorNumber,threshold,axis): - self._addMethod('sensor') - return self.startSensingThreshold(sensorNumber,threshold,axis) - -def startSensingTimed(self,sensorNumber,delayTime): - self._addMethod('sensor') - return self.startSensingTimed(sensorNumber,delayTime) - -def checkBluetoothState(self): - self._addMethod('bluetooth') - return self.checkBluetoothState() - -def toggleBluetoothState(self,enabled=None,prompt=True): - self._addMethod('bluetooth') - return self.toggleBluetoothState(enabled,prompt) - -_addMethod('accessibility') -_addMethod('floatView/__init__') - -#add Class - -def cipherInit( self, key, algorithm = "AES/CBC/PKCS5Padding", encodingFormat = "", initialVector = "" ): - self._addClass('cipher') - return self.cipherInit( key, algorithm, encodingFormat, initialVector ) - -def documentTreeShowOpen( self, rootPath='/sdcard' ): - self._addClass('documentFile') - return self.documentTreeShowOpen( rootPath ) - -def speechToText(self, RecordSecond = 10, AmrFile = None, Language = None): - self._addClass('pgptai') - return self.speechToText(RecordSecond, AmrFile, Language) - -def textToSpeech(self, Text, AutoPlay = True, WavFile = None, VoiceName = None): - self._addClass('pgptai') - return self.textToSpeech(Text, AutoPlay, WavFile, VoiceName) - -def notebookOpen(self, path = None): - os = sl4a.os - npy = os.environ["HOME"]+"/bin/NoteBook.py" - if path: - return self._rpc("executeQPy", npy, os.path.abspath(path)) - return self._rpc("executeQPy", npy) - -#add Constant - -@property -def R(self): - from . import R - R=R.R() - Android.R=R - return R - -@property -def provider(self): - from . import provider as p - p=p.provider() - Android.provider=p - return p - -@property -def Intent(self): - from . import Intent as i - Android.Intent=i - return i diff --git a/qsl4a-src/androidhelper/fun/usb.py b/qsl4a-src/androidhelper/fun/usb.py deleted file mode 100644 index 6b5e701..0000000 --- a/qsl4a-src/androidhelper/fun/usb.py +++ /dev/null @@ -1,19 +0,0 @@ -#以下 SL4A函数 由 Xmile 修复 - -def usbserialGetDeviceList(self): - return self._rpc("usbserialGetDeviceList") - -def usbserialDisconnect(self, connID = ""): - return self._rpc("usbserialDisconnect", connID) - -def usbserialConnect(self, hash = "", options =""): - return self._rpc("usbserialConnect", hash, options) - -def usbserialWrite(self, ascii, connID = ""): - return self._rpc("usbserialWrite", ascii, connID) - -def usbserialReadReady(self, connID = ""): - return self._rpc("usbserialReadReady", connID) - -def usbserialRead(self, connID = "", bufferSize = 4096): - return self._rpc("usbserialRead", connID, bufferSize) diff --git a/qsl4a-src/androidhelper/pgptai.py b/qsl4a-src/androidhelper/pgptai.py deleted file mode 100644 index 4e7b0a4..0000000 --- a/qsl4a-src/androidhelper/pgptai.py +++ /dev/null @@ -1,149 +0,0 @@ -try: - from requests import request -except: - error=Exception('Please pip install apigptcloud .') - def request(*a,**b):raise error - print(error) - -from time import sleep -from .sl4a import Result -from .__init__ import Android -from os.path import basename,splitext,abspath -from os import environ -from traceback import format_exc -import configparser - -def read_conf_file(conf_file_path, section, key): - # 创建配置解析器对象 - config = configparser.ConfigParser() - # 读取配置文件 - config.read(conf_file_path) - # 获取指定 section 下的指定 key 的值 - value = config.get(section, key) - return value - -try: - keyOfSpeech = read_conf_file(environ['ANDROID_PUBLIC']+"/files/apigpt.conf", "speech", "speech_key") -except: - keyOfSpeech = "MWYwZWVmNWQ0MGRiNGE5ZjBmZjFiNTUxMjVlMGNiYWE=" - -headers = { - 'accept': 'application/json', - "Authorization": "Bearer "+keyOfSpeech -} - -urlPref = 'https://ai.pgpt.cloud/v1/speech/' - -defaultLanguage = { - 'en':'en-US', - 'zh':'zh-CN' -} - -defaultVoice = { - 'en':'en-US-JennyNeural', - 'zh':'zh-CN-XiaoxiaoNeural' -} - -class pgptai: - - def __init__( this, sup ): - for i in dir(this): - if i[0] != '_': - exec(f'pgptai.{i}.__doc__=Android.{i}.__doc__\nsup.{i}=this.{i}') - this._rpc = sup._rpc - this._write = sup._write - this._flush = sup._flush - this._readline = sup._readline - this._sup = sup - - def speechToText(self, RecordSecond = 10, AmrFile = None, Language = None): - if not AmrFile: - AmrFile = environ['TMP']+'/tmp.amr' - AmrFile = abspath(AmrFile) - if not RecordSecond: - self._sup._id += 1 - else: - response = self._rpc('recorderStartMicrophone',AmrFile) - if response.error: - return Result(self._sup._id,None,'Record Start : '+response.error) - sleep(RecordSecond) - response = self._rpc('recorderStop') - if response.error: - return Result(self._sup._id,None,'Record Stop : '+response.error) - url = urlPref + 'speech2text' - if not Language: - Language = defaultLanguage[environ['LANG'][:2]] - params = { - 'lang': Language, - } - files = [ - ( 'audio', - ( - basename(AmrFile), - open(AmrFile,'rb'), - 'audio/'+splitext(AmrFile)[1][1:] - ))] - id = self._sup._id - try: - response = request("POST", url, headers=headers, params=params, files=files) - except: - return Result(id,None,format_exc()) - status_code=response.status_code - content=str(response.content,'utf-8') - try: - content=eval(content) - except: - pass - if status_code!=200: - try: - content=content['error'] - except: - pass - return Result(id,None,content) - else: - return Result(id,content['text'],None) - - def textToSpeech(self, Text, AutoPlay = True, WavFile = None, VoiceName = None): - if not VoiceName: - VoiceName = defaultVoice[environ['LANG'][:2]] - url = urlPref + 'text2speech' - data = { - 'voice_name': VoiceName, - 'text': Text, - } - id = self._sup._id - try: - response = request('POST', url, headers=headers, json=data) - except: - return Result(id,None,format_exc()) - status_code=response.status_code - content=str(response.content,'utf-8') - try: - content=eval(content) - except: - pass - if status_code!=200: - try: - content=content['error'] - except: - pass - return Result(id,None,content) - else: - if not WavFile: - WavFile = environ['TMP']+'/tmp.wav' - try: - url=content['url'] - wav=request('GET',url).content - open(WavFile,'wb').write(wav) - content['WavFile']=WavFile - if AutoPlay: - self._rpc('mediaPlay',WavFile) - id = self._sup._id - self._sup._id += 1 - return Result(id,content,None) - except: - id = self._sup._id - self._sup._id += 1 - return Result(id,None,str(content)+'\n'+format_exc()) - -# by 乘着船 @ Bilibili at 2023 diff --git a/qsl4a-src/androidhelper/provider/Settings.py b/qsl4a-src/androidhelper/provider/Settings.py deleted file mode 100644 index 432a75c..0000000 --- a/qsl4a-src/androidhelper/provider/Settings.py +++ /dev/null @@ -1,21 +0,0 @@ -'android.provider.Settings Constants, for more Settings Constants, please refer https://developer.android.google.cn/reference/android/provider/Settings.html .\n\nYou can use them like droid.startActivity(droid.provider.Settings.ACTION_SETTINGS) .' - -ACTION_SETTINGS='android.settings.SETTINGS' -ACTION_SOUND_SETTINGS='android.settings.SOUND_SETTINGS' -ACTION_WIRELESS_SETTINGS='android.settings.WIRELESS_SETTINGS' -ACTION_APPLICATION_SETTINGS='android.settings.APPLICATION_SETTINGS' -ACTION_BLUETOOTH_SETTINGS='android.settings.BLUETOOTH_SETTINGS' -ACTION_CAPTIONING_SETTINGS='android.settings.CAPTIONING_SETTINGS' -ACTION_DATA_USAGE_SETTINGS='android.settings.DATA_USAGE_SETTINGS' -ACTION_DATE_SETTINGS='android.settings.DATE_SETTINGS' -ACTION_DEVICE_INFO_SETTINGS='android.settings.DEVICE_INFO_SETTINGS' -ACTION_DISPLAY_SETTINGS='android.settings.DISPLAY_SETTINGS' -ACTION_HARD_KEYBOARD_SETTINGS='android.settings.HARD_KEYBOARD_SETTINGS' -ACTION_INPUT_METHOD_SETTINGS='android.settings.INPUT_METHOD_SETTINGS' -ACTION_INTERNAL_STORAGE_SETTINGS='android.settings.INTERNAL_STORAGE_SETTINGS' -ACTION_LOCALE_SETTINGS='android.settings.LOCALE_SETTINGS' -ACTION_LOCATION_SOURCE_SETTINGS='android.settings.LOCATION_SOURCE_SETTINGS' -ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS='android.settings.IGNORE_BATTERY_OPTIMIZATION_SETTINGS' -ACTION_BATTERY_SAVER_SETTINGS="android.settings.BATTERY_SAVER_SETTINGS" -ACTION_ACCESSIBILITY_SETTINGS = "android.settings.ACCESSIBILITY_SETTINGS" -ACTION_AIRPLANE_MODE_SETTINGS = "android.settings.AIRPLANE_MODE_SETTINGS" diff --git a/qsl4a-src/androidhelper/provider/__init__.py b/qsl4a-src/androidhelper/provider/__init__.py deleted file mode 100644 index 50d4ad7..0000000 --- a/qsl4a-src/androidhelper/provider/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -'android.provider module, include Settings .' -class provider: - @property - def Settings(self): - from . import Settings as s - provider.Settings=s - return s -provider.__doc__=__doc__ \ No newline at end of file diff --git a/qsl4a-src/androidhelper/sensor.py b/qsl4a-src/androidhelper/sensor.py deleted file mode 100644 index 6189c09..0000000 --- a/qsl4a-src/androidhelper/sensor.py +++ /dev/null @@ -1,52 +0,0 @@ -_doc_='''readSensors() -Returns the most recently recorded sensor data. - -sensorsGetAccuracy() -Returns the most recently received accuracy value. - -sensorsGetLight() -Returns the most recently received light value. - -sensorsGetStepCounter() -Returns the most recently Step Counter. - -sensorsReadGyroscope() -Returns the most recently received gyroscope value. - -sensorsReadAccelerometer() -Returns the most recently received accelerometer values. -returns: (List) a List of Floats [(acceleration on the) X axis, Y axis, Z axis]. - -sensorsReadMagnetometer() -Returns the most recently received magnetic field values. -returns: (List) a List of Floats [(magnetic field value for) X axis, Y axis, Z axis]. - -sensorsReadOrientation() -Returns the most recently received orientation values. -returns: (List) a List of Doubles [azimuth, pitch, roll]. - -stopSensing() -Stops collecting sensor data.''' - -_g=_doc_.split('\n\n') - -for _t in _g: - _i=_t[:_t.find('(')] - exec(f''' -def {_i}(self): - return self._rpc("{_i}") -{_i}.__doc__=_t -''') - -def startSensingThreshold(self,sensorNumber,threshold,axis): - return self._rpc("startSensingThreshold",sensorNumber,threshold,axis) - -def startSensingTimed(self,sensorNumber,delayTime): - return self._rpc("startSensingTimed",sensorNumber,delayTime) - -_g='\n\n' -for _i in ('startSensingTimed','startSensingThreshold'): - exec(f'{_i}.__doc__=_self.{_i}.__doc__') - exec(eval(f'{_i}.__doc__.rsplit(_g,1)[1]')) - -del _g,_i,_t,_doc_ \ No newline at end of file diff --git a/qsl4a-src/androidhelper/sl4a.py b/qsl4a-src/androidhelper/sl4a.py deleted file mode 100644 index e5fd2fc..0000000 --- a/qsl4a-src/androidhelper/sl4a.py +++ /dev/null @@ -1,40 +0,0 @@ -import collections -from json import loads,dumps -import os -import socket - -PORT = os.environ.get('AP_PORT') -HOST = os.environ.get('AP_HOST') -HANDSHAKE = os.environ.get('AP_HANDSHAKE') -Result = collections.namedtuple('Result', 'id,result,error') - -class Android(object): - - def __init__(self, addr=None): - if addr is None: - addr = HOST, PORT - client = socket.create_connection(addr).makefile("rw") - self._write = client.write - self._flush = client.flush - self._readline = client.readline - self._id = 0 - self._authenticate(HANDSHAKE) - - def _rpc(self, method, *args): - self._write(dumps({ - 'id': self._id, - 'method': method, - 'params': args - }) + '\n') - self._flush() - self._id += 1 - result = loads(self._readline()) - if result['error'] is not None: - print(result['error']) - # namedtuple doesn't work with unicode keys. - return Result(result['id'], - result['result'], - result['error'] ) - - def __getattr__(self, name): - return lambda *args:self._rpc(name, *args) diff --git a/qsl4a-src/fullscreenwrapper.py b/qsl4a-src/fullscreenwrapper.py deleted file mode 100644 index 10154cd..0000000 --- a/qsl4a-src/fullscreenwrapper.py +++ /dev/null @@ -1,570 +0,0 @@ -#Created on Jul 19, 2012 -#author: Hariharan Srinath -#修改: 乘着船 2021-2025 - -import abc,pickle,json,sys,time,os,hashlib - -class BaseDict(dict): - ''' - implements a dictionary that can be accessed by BaseDict[key] as well as by BaseDict.key to allow more pythonic access - credits: BaseDict pattern at http://code.activestate.com/recipes/473790/ under PSF license - ''' - - def __init__(self, data=None): - if data: - dict.__init__(self, data) - else: - dict.__init__(self) - - def __setattr__(self, name, val): - if name in self.__dict__: - self.__dict__[name]= val - else: - self[name] = val - - def __getattr__(self, name): - if name in self.__dict__: - return self.__dict__[name] - else: - return self[name] - - def setDict(self, name, val): - ''' - setDict(name, val): Assign *val* to the key *name* of __dict__. - >>> bd.setDict('height', 160) - {} - >>> bd.getDict()['height'] - 160 - ''' - self.__dict__[name] = val - return self - - def getDict(self): - ''' - Return the internal __dict__. - >>> bd.setDict('height', 160) - {} - >>> bd.getDict()['height'] - 160 - ''' - return self.__dict__ - - def setItem(self, name, val): - ''' - Set the value of dict key *name* to *val*. Note this dict - is not the __dict__. - ''' - self[name] = val - return self - - def __getstate__(self): - ''' Needed for cPickle in .copy() ''' - return self.__dict__.copy() - - def __setstate__(self,dict): - ''' Needed for cPickle in .copy() ''' - self.__dict__.update(dict) - - def copy(self): - ''' - Return a copy. - ''' - return pickle.loads(pickle.dumps(self)) - - -class EventHandler(object): - ''' - Defines an SL4A event handler and provides a matching function to compare vs. Android.eventPoll().result - - SL4A eventdata returned by Android.eventWait() or Android.eventPoll().result in general take the form of a dict: - {"data":{"attribute1":value,"attribute2":value}, "name":"event_name", "time":eventtime} - - The EventHandler object consists of an event_name, a compare_attribute to look for within the "data" dict & a - compare_value which the compare_attribute will get matched against. It also has optionally an event_handler_fn - which stores a reference to the method to be called and the reference to the view referred to by the event. - - fullscreenwrapper2 module pre-defines click_EventHandler, itemclick_EventHandler, itemlongclick_EventHandler and key_EventHandler which are - commonly used with Layout views for your convenience - - When the FullScreenWrapper2App class which handles events finds a match, it will call the function defined in the - EventHandler passing the view & a copy of the eventdata. The event handler method signature should therefore be: - def event_handler_function(self, view, eventdata): - ''' - - def __init__(self,event_name, compare_attribute,compare_value,view = None, handler_function=None): - ''' - creates an SL4A event handler - - SL4A eventdata returned by Android.eventWait() or Android.eventPoll().result in general take the form of a dict: - {"data":{"attribute1":value,"attribute2":value}, "name":"event_name", "time":eventtime} - - The EventHandler object consists of an event_name, a compare_attribute to look for within the "data" dict & a - compare_value which the compare_attribute will get matched against. It also has optionally an event_handler_fn - which stores a reference to the method to be called and the reference to the view referred to by the event. - - The compare_attribute can be None. if this is the case, then the event_name alone is matched. You can use this feature - to catch other SL4A API events like sensor events - ''' - self.view = view - self.event_name = event_name - self.compare_attribute = compare_attribute - self.compare_value = compare_value - self.event_handler_fn = handler_function - - def match_event_data(self, event_data): - ''' - Provides a matching function to compare event handler vs. data returned by Android.eventPoll().result or Android.eventWait() - - SL4A eventdata returned by Android.eventWait() or Android.eventPoll().result in general take the form of a dict: - {"data":{"attribute1":value,"attribute2":value}, "name":"event_name", "time":eventtime} - - The function first matches event_data[event_name] and then tries to match event_data["data"][compare_attribute] to compare_value - returns True on match, False on no-match or event not found - - The compare_attribute can be None. if this is the case, then the event_name alone is matched. You can use this feature - to catch other SL4A API events like sensor events - ''' - try: - if event_data["name"]==self.event_name: - if self.compare_attribute != None: - if event_data["data"][self.compare_attribute]==self.compare_value: - return True - else: - return True - except: - return False - else: - return False - - def __str__(self): - ''' - convenience function for debugging - ''' - return str(self.event_name)+":"+str(self.compare_attribute)+"="+str(self.compare_value) - -class click_EventHandler(EventHandler): - ''' - predefined click event handler for use with Views - - This is the event handler to typically associate with TextView, Button, ImageView etc. You only need to pass the view to - link the click event to & the handler function & rest of event handler initialization is handled automatically - ''' - EVENT_NAME = "click" - COMPARE_ATTRIBUTE = "id" - - def __init__(self,view, handler_function=None): - ''' - predefined click event handler for use with Views - - This is the event handler to typically associate with TextView, Button, ImageView etc. You only need to pass the view to - link the click event to & the handler function & rest of event handler initialization is handled automatically - ''' - super(click_EventHandler,self).__init__(self.EVENT_NAME,self.COMPARE_ATTRIBUTE,view.view_id,view,handler_function) - -class itemclick_EventHandler(EventHandler): - ''' - predefined itemclick event handler for use with Views - - This is the event handler to typically associate with ListView. You only need to pass the ListView to link the itemclick event - to & the handler function & rest of event handler initialization is handled automatically - ''' - EVENT_NAME = "itemclick" - COMPARE_ATTRIBUTE = "id" - - def __init__(self,view,handler_function=None): - super(itemclick_EventHandler,self).__init__(self.EVENT_NAME,self.COMPARE_ATTRIBUTE,view.view_id,view,handler_function) - -class itemlongclick_EventHandler(EventHandler): - ''' - predefined itemlongclick event handler for use with Views - - This is the event handler to typically associate with ListView. You only need to pass the ListView to link the itemlongclick event - to & the handler function & rest of event handler initialization is handled automatically - ''' - EVENT_NAME = "itemlongclick" - COMPARE_ATTRIBUTE = "id" - - def __init__(self,view,handler_function=None): - super(itemlongclick_EventHandler,self).__init__(self.EVENT_NAME,self.COMPARE_ATTRIBUTE,view.view_id,view,handler_function) - -class key_EventHandler(EventHandler): - ''' - predefined key event handler for use with Layout. defaults to Back Key with key_id = "4" - - This is the event handler to typically associate with a layout. You need to pass key_id to associate with (defaults to - back key = "4") & the handler function & rest of event handler initialization is handled automatically - ''' - EVENT_NAME = "key" - COMPARE_ATTRIBUTE = "key" - - def __init__(self,key_match_id="4",view=None,handler_function=None): - super(key_EventHandler,self).__init__(self.EVENT_NAME,self.COMPARE_ATTRIBUTE,key_match_id,view,handler_function) - -class View(object): - ''' - Defines a View and provides pythonic access to its properties & a mechanism to define events. - - You don't create views yourself. They are created by FullScreenWrapper2App.show_layout() after showing the xml - and are populated in the Layout.views which is a BaseDict => ie. a dict that allows access by both [key] and .key - - You can access a view's properties simply by Layout.views.viewname.property to get & set property. Doing this - calls the appropriate SL4A api function like fullSetProperty() - - To add and remove events, use the View.add_event() and View.remove_event() methods. To set the contents of - a ListView, use the View.set_listitems() method - ''' - def __init__(self,view_id, view_type): - ''' - View constructer called with view_id & view_type. DO NOT create a view yourself. - - Views are created by FullScreenWrapper2App.show_layout() after showing the xml and are populated - in the Layout.views which is a BaseDict => ie. a dict that allows access by both [key] and .key - ''' - self.view_type = view_type - self.view_id = view_id - self._events = {} - - def add_event(self, eventhandler): - ''' - Used to add an EventHandler to the view. - - You would typically add one of click_EventHandler or itemclick_EventHandler or itemlongclick_EventHandler (for List Views) - to a view - ''' - self._events[eventhandler.event_name]=eventhandler - - def remove_event(self,event_name): - ''' - removes an event added previously by matching the event_name. Use this to temporarily disable a view's click event - ''' - self._events.pop(event_name) - - def set_listitems(self,listitems,isHtml=False,listType=0): - ''' - sets a list for a ListView. Takes a list of string as input , - string can be plain text ( isHtml = False ) or HTML ( isHtml = True ) - listType default is SIMPLE list (0) - ''' - FullScreenWrapper2App.set_list_contents(self.view_id, listitems, isHtml, listType) - - def __setattr__(self, name, value): - ''' - This allows pythonic access to setting a View's properties by calling SL4A api - - For eg: Layout.views.viewname.color = "#FFFFFFFF" - ''' - if name in ("view_type","view_id","_events"): - object.__setattr__(self,name,value) - else: - #sys.stderr.write("calling sl4a to set name:"+str(name)+" value:"+value+"\n") - return FullScreenWrapper2App.set_property_value(self.id, name, value) - - def __getattr__(self, name): - ''' - This allows pythonic access to getting a View's properties by calling SL4A api - - For eg: buttontext = Layout.views.buttonname.text - ''' - #sys.stderr.write("calling sl4a to get name:"+str(name)+"\n") - return FullScreenWrapper2App.get_property_value(self.view_id, name) - - def __str__(self): - ''' - str(View) will return the View.text - ''' - try: - return self.text - except AttributeError: - return None - -class Layout(object): - ''' - Defines a "screen" with an xml layout that contains views. Layout is a abstract class - you MUST derive your own Layout class. - - To use a Layout, you need to first derive a class with Layout as a base class: MyLayout(Layout) and define the functions on_show(self) - and on_close(self). in your MyLayout.__init__(), you MUST include a call to super(MyLayout,self).__init__(xml, title, theme) - - the xml property stores the xml text. This is used by FullScreenWrapper2App.show_layout() to actually display the layout. - - The layout contains importantly a BaseDict called views. A BaseDict is a dict that allows access to members by either [key] or .key - - IMPORTANT: The views BaseDict is populated by FullScreenWrapper2App.show_layout() once the xml is displayed on the screen. Your layout's Views that have an id - only become accessible once the xml is displayed & the Layout.on_show() function is caled by the framework. DO NOT try to access views in the __init__() and - put all your view initialization code & event handler attachment in the on_show() function. - - The views BaseDict allows you to access & modify properties of your views & allows event based interaction. You would typically access view - properties as Layout.views.view_id.property with the FullScreenWrapper2 framework making the appropriate SL4A api calls - to access the property. To set events for the views, use Layout.views.view_id.add_event(EventHandler) - - The FullScreenWrapper2App actually stores layout objects in a stack allowing you to seamlessly the right parent layout on closing a child layout. This - lets you build a natural interaction using the "back" key. Note however that every time a layout is shown, its views are created afresh & the Layout.views - BaseDict is cleared & re-populated and the Layout.on_show() function is called. This is why you should put all your view initialization & event handler setup - code in Layout.on_show() - - Layout.on_close method MUST also be defined - though it can simply be a 1 line function containing pass. This is called when a layout is either closed - or a child layout is opened. This method to save state. - - Layouts also allow you to set "Layout" events through Layout.add_event() - you would typically use this for things like "back key press" or even for - other events which are accessible through the SL4A EventFacade's event system like sensor data. For catching these events, you would typically set - EventHandler.compare_attribute to None. - - Layout events are internally handled by adding a special "layout" view to the views collection identified by a hashtag. You should not yourself - access this special view. - ''' - __metaclass__ = abc.ABCMeta - defaultTheme = 0 - - def __init__(self,xml,title=None,theme=None): - ''' - creates a layout and stes its xml,title,theme, initializes the views collection - - NOTE that this DOES NOT display the layout and the layout Views are also not populated. The special "layout" view for handling Layout - evnets however is created here - ''' - self.uid = hashlib.md5(str(str(title)+str(os.getpid())+str(time.time())).encode("UTF-8")).hexdigest() - self.title = title - self.xml = xml - if theme==None: - self.theme = Layout.defaultTheme - else: - self.theme = theme - self.views = BaseDict() - self._reset() - - def _reset(self): - ''' - This function will clear the views collection & add the special "Layout" view - which is used to handle layout events internally - ''' - self.views.clear() - #adds a dummy view representing the layout for event management - self.views[self.uid]= View(self.uid,"Layout") - - def add_event(self, eventhandler): - ''' - This function adds a Layout event. This event is added to the special "layout" view in the views collection - ''' - self.views[self.uid].add_event(eventhandler) - - def remove_event(self,event_name): - ''' - This function removes a Layout event by event name. This event is actually stored in the special "layout" view in the views collection - ''' - self.views[self.uid].remove_event(event_name) - - @abc.abstractmethod - def on_show(self): - ''' - The on_show method is called after your layout is displayed to allow you to initialize your layout's views' attributes & setup event handlers. - - on_show is an abstract method which MUST be defined in the your layout class. FullScreenWrapper2App.show_layout() displays the layout & populates the views BaseDict collection - and then calls Layout.on_show() letting you do your view initializations & setup event handlers. This function is called every time a view is displayed - for eg. after a child - layout is closed & the parent layout shown again on screen - - If you have saved state in Layout.on_close() be sure to read back state & populate data in your layout's views in on_show() - ''' - pass - - @abc.abstractmethod - def on_close(self): - ''' - The on_close method MUST be defined & is called both when your layout is closed or before displaying a child layout to let you save state. - - If you're saving state here, you can read back state on on_show() method - ''' - pass - - -class FullScreenWrapper2App(object): - ''' - FullScreenWrapper2App implements the "App" incorporating an eventloop & a layout stack with methods to display & close layouts and access SL4A FullScreenUI API functions - - You SHOULD NOT instantiate a FullScreenWrapper2App but rather, simply call its class methods. To use the app, you first need to call FullScreenWrapper2App.initialize(android_instance) with the droid = android.Android() - object that you have created in you program. This is always subsequently accessible by FullScreenWrapper2App.get_android_instance() - - You can then call FullScreenWrapper2App.show_layout() and FullScreenWrapper2App.close_layout() to show and close layouts respectively. FullScreenWrapper2App places layouts in an internal stack. This lets the framework seamlessly handle - parent->show child->close child->show parent type of transitions simplifying your code. It also gives you a method to exit the app by calling FullScreenWrapper2App.exit_FullScreenWrapper2App() at any time. This internally works by signalling - the eventloop to terminate the loop by posting an internal event - - the internal function called by FullScreenWrapper2App.show_layout() and close_layout() actually populates the layout's views once it is shown & also calls the Layout.on_show() function - - Once you have called show_layout() and your first layout is displayed on screen with its view properties & event handlers set, you should call FullScreenWrapper2App.eventloop() to start the event loop. The event loop will keep polling for - the event queue and dispatch events to the appropriate handler functions - - The FullScreenWrapper also defines a few "convenience" functions which are used to set and access fullscreen properties via SL4A api calls - ''' - _android_instance = None - _layouts = [] - - SHOW_LAYOUT_PUSH_OVER_CURRENT = 0 - SHOW_LAYOUT_REPLACING_CURRENT = 1 - _SHOW_LAYOUT_POP_CURRENT = 2 - - #Internal Signal for exit FullScreenWrapper2App or insert operation before eventPoll function . - #SIGNAL_DATA stores signals of exit or insert function . - #EVENT_DATA stores event queue - SIGNAL_DATA=[] - EVENT_DATA=[] - - @classmethod - def initialize(cls, android_instance): - ''' - You MUST call this first with your droid = android.Android() instance before calling any other function - ''' - cls._android_instance = android_instance - - @classmethod - def get_android_instance(cls): - ''' - this allows you to access the android.Android() instance set in FullScreenWrapper2App.initialize() at any time - ''' - if cls._android_instance!=None: - return cls._android_instance - else: - raise RuntimeError("You need to call FullScreenWrapper2App.initialize(android_instance) first") - - @classmethod - def show_layout(cls, layout, show_mode = SHOW_LAYOUT_REPLACING_CURRENT): - ''' - This will show the layout, set the title and theme, clean & re-populate layout's views BaseDict collection & call the Layout.on_show() - - this will also push the layout to the top FullScreenWrapper2App._layouts[] stack. If there is already a parent layout showing, then - this will call the parent layout's on_close() function to let the parent layout save state - ''' - - if show_mode in (0,1,2): # show_mode == cls.SHOW_LAYOUT_PUSH_OVER_CURRENT or show_mode == cls.SHOW_LAYOUT_REPLACING_CURRENT or show_mode == cls._SHOW_LAYOUT_POP_CURRENT - _layout = cls._layouts - _android = cls.get_android_instance() - - curlayoutidx = len(_layout)-1 - - if(curlayoutidx > -1): - _layout[curlayoutidx].on_close() - - _android.fullShow(layout.xml,layout.title,layout.theme) - - viewsdict = _android.fullQuery().result - layout._reset() - - for viewname in iter(viewsdict): - layout.views[viewname] = View(viewname, viewsdict[viewname]["type"]) - - if show_mode == cls.SHOW_LAYOUT_PUSH_OVER_CURRENT: - _layout.append(layout) - elif show_mode == cls.SHOW_LAYOUT_REPLACING_CURRENT: - if(curlayoutidx > -1): - _layout.pop() - _layout.append(layout) - elif show_mode == cls._SHOW_LAYOUT_POP_CURRENT: - if(curlayoutidx > -1): - _layout.pop() - - layout.on_show() - - @classmethod - def close_layout(cls): - ''' - This will first call a layout's on_close() function to help save state & then close the active layout. - - If the layout being closed is a child layout, then this will pop the child layout from the FullScreenWrapper2App._layouts[] stack - and show the parent immediately below the child layout in the stack. - ''' - curlayoutidx = len(cls._layouts)-1 - - if curlayoutidx >0: - cls.show_layout(cls._layouts[curlayoutidx-1], cls._SHOW_LAYOUT_POP_CURRENT) - elif curlayoutidx == 0: - cls.get_android_instance().fullDismiss() - cls.exit_FullScreenWrapper2App() - - @classmethod - def exit_FullScreenWrapper2App(cls): - ''' - convenience function to exit the app. this works by signalling the eventloop to stop - ''' - cls.get_android_instance().fullDismiss() - cls.SIGNAL_DATA.append(os.getpid()) - - @classmethod - def insert_function(cls, function,*args,**kwargs): - ''' - Insert operations before FullScreenWrapper2App eventloop function - ''' - cls.SIGNAL_DATA.append((function,args,kwargs)) - - @classmethod - def get_event(cls): - ''' - Check FullScreenWrapper2App has a waiting event or not . - ''' - cls.EVENT_DATA.extend(cls.get_android_instance().eventPoll()) - return cls.EVENT_DATA - - @classmethod - def eventloop(cls): - ''' - The main event loop to catch & dispatch events in the active/topmost layout in the _layouts[] stack & its views. - - Call this once your first layout's event handlers are setup from your main() program. This catches & dispatches events - by matching EventHandlers in the ACTIVE/TOPMOST layout int he _layouts[] stack & its views. - - Note that only the active layout & its views are matched with events. This function also looks for "exit" signal - which can be raised by calling exit_FullScreenWrapper2App() to terminate the event loop. - ''' - if len(cls._layouts)<1: - raise RuntimeError("Trying to start eventloop without a layout visible") - - evt=True - while(True): - for evt in cls.SIGNAL_DATA: - if type(evt)==int: - evt=None - break - else: - function,args,kwargs=evt - function(*args,**kwargs) - cls.SIGNAL_DATA.clear() - if evt==None: - break - - evt=cls.EVENT_DATA - evt.extend(cls.get_android_instance().eventPoll().result) - while len(evt)>0: - eventdata=evt.pop(0) - - #this corrects an eventpost issue where an extra "" wraps the json - try: - if type(eventdata["data"]) != dict: - eventdata["data"]=json.loads(eventdata["data"]) - except: - pass - - #sys.stderr.write("in event loop-got an event\n") - #sys.stderr.write(str(eventdata)+"\n") - - curlayout = cls._layouts[len(cls._layouts)-1] - - for viewname in iter(curlayout.views): - view = curlayout.views[viewname] - #sys.stderr.write("Checking with"+ view.view_id+"\n") - for eventname in iter(view._events): - event = view._events[eventname] - if event.match_event_data(eventdata): - if event.event_handler_fn != None: - event.event_handler_fn(event.view, eventdata) - event_handled = True - break - - @classmethod - def set_list_contents(cls, *a, **k): - return cls.get_android_instance().fullSetList(*a,**k) - - @classmethod - def set_property_value(cls, id, property, value): - '''Set the value of an XML view's property''' - return cls.get_android_instance().fullSetProperty(id, property, value) - - @classmethod - def get_property_value(cls, id, property): - '''Get the value of a given XML view's property''' - return cls.get_android_instance().fullGetProperty(id,property).result \ No newline at end of file diff --git a/source/en/AIPyApp.md b/source/en/AIPyApp.md deleted file mode 100644 index 33d2a01..0000000 --- a/source/en/AIPyApp.md +++ /dev/null @@ -1,73 +0,0 @@ -# AIPyApp - AI-Powered Program Generator - -AIPyApp is an intelligent tool in QPython that uses AI to automatically generate Python programs from natural language instructions. - -![AIPyApp](static/aipyapp_demo.jpg) - -## Overview - -AIPyApp transforms the way you write code - simply describe what you want in natural language, and the AI will generate the Python program for you. QPython will also feature **AIPy Academy** - a platform offering Python programming courses tailored for the AI era. - -## Installation - -### Step 1: Launch from Dashboard - -1. Open QPython and go to the **Dashboard** -2. **Long press** the start button - -If AIPyApp is not installed, you will be prompted to confirm the installation. Press **Enter** to proceed. - -QPython will automatically download and install the required dependencies from PYPI. Please wait patiently for the installation to complete. - -### Step 2: Restart AIPyApp - -After installation, return to the QPython Dashboard and **long press** the start button again to launch AIPyApp. - -## Configuration - -### Setting Up Your AI Key - -On the first launch, you need to provide an AI API key: - -1. **Register at PGPT**: Create an account at [https://user.pgpt.cloud](https://user.pgpt.cloud) to generate your AI key -2. **Advanced Option**: AIPyApp also supports custom AI keys from OpenAI, Deepseek, and other providers (see advanced tutorials for details) - -### Entering Your AI Key - -1. Long press on the input prompt -2. Select **Paste** from the popup menu -3. Press **Enter** to confirm - -Your AI key will be saved for future sessions. - -## Using AIPyApp - -After configuration, you enter the AIPyApp console mode. Simply type your instructions in natural language! - -### Example Command - -Try entering: - -``` -Use QSL4A to create a HELLO QPY program as a demo -``` - -AIPyApp will: -1. Understand your natural language request -2. Generate the corresponding Python code -3. Execute the program automatically - -That's it - you've created a working Python program without writing any code! - -## Demo - -The example above demonstrates how AIPyApp can: -- Understand Chinese instructions -- Generate QSL4A-based Python code -- Run the program immediately - -Explore AIPyApp to discover more capabilities and start building Python programs effortlessly. - -## Learn More - -Stay tuned for **AIPy Academy** at [aipy.org](https://aipy.org) - upcoming courses on learning and using Python programming in the AI era. diff --git a/source/en/GraphicalInterface.md b/source/en/GraphicalInterface.md deleted file mode 100644 index 1a27009..0000000 --- a/source/en/GraphicalInterface.md +++ /dev/null @@ -1,67 +0,0 @@ -# Graphical Interface (Turtle & Tkinter) - -This guide explains how to enable graphical interface support (Turtle and Tkinter) in QPython on Android devices. - -![QPython Graphical Interface](static/qpython_graphical_interace_demo.jpg) - -## Overview - -QPython can run Turtle and Tkinter applications, but requires additional software to provide graphical display support on Android. - -## Prerequisites - -Before starting, you need to download the following resources: - -1. **Xserver.apk** - A companion app that provides graphical support for Turtle/Tkinter - - Download from: [QPythonProject/Extra on Google Drive](https://www.qpython.org/en/#download-resources) -2. **Turtle & Tkinter QPython graphical interface extension** - Install via QPython's QPYPI - -## Installation Steps - -### Step 1: Install Xserver - -Download and install Xserver.apk from the QPython Extra resources directory on Google Drive. - -### Step 2: Install QPython Extension - -Open QPython and navigate to QPYPI. Find and install the **Turtle & Tkinter QPython graphical interface** extension. - -### Step 3: Configure Xserver Battery Settings - -To prevent Xserver from being killed when running in the background: - -1. Go to your device's **Settings** > **Apps** > **Xserver** -2. Find **Battery** settings -3. Set battery management to **"Unrestricted"** or **"No restrictions"** - -This ensures Xserver continues running when switched to background. - -### Step 4: Configure QPython Battery Settings (Recommended) - -Similarly, set QPython's battery management to **"Unrestricted"** to prevent process termination: - -1. Go to **Settings** > **Apps** > **QPython** -2. Find **Battery** settings -3. Set battery management to **"Unrestricted"** - -### Step 5: Launch Xserver - -Start the Xserver app and switch it to run as a background task before running your Turtle/Tkinter application. - -## Running Turtle/Tkinter Applications - -After completing the setup: - -1. Ensure Xserver is running in the background -2. Run your Turtle or Tkinter application in QPython -3. Switch to Xserver to view the graphical output - -## Demo Program - -You can download and try the **Turtle Draw Doraemon** demo program from QPYPI's first extension section of QPython App to verify your setup. - -## Troubleshooting - -- **Black screen**: Ensure Xserver is running before starting your application -- **Application crashes**: Check that both QPython and Xserver have unrestricted battery settings -- **No display**: Verify the Turtle/Tkinter extension is properly installed via QPYPI diff --git a/source/en/Notebook.md b/source/en/Notebook.md deleted file mode 100644 index e9fbfbd..0000000 --- a/source/en/Notebook.md +++ /dev/null @@ -1,47 +0,0 @@ -# Notebook - -QPython integrates Jupyter Notebook, providing a powerful interactive environment for data science, scientific computing, and AI development on Android devices. - -## Overview - -QPython comes with a built-in Jupyter Notebook application that allows you to create and run interactive Python notebooks directly on your Android device. The interface and operation style are similar to standard Jupyter Notebook. - -## Available Libraries - -QPython supports extensive mathematical, scientific, and AI-related libraries that are well-suited for Notebook environments. Install them via QPYPI as needed: - -- **Matplotlib** - Plotting and visualization -- **Seaborn** - Statistical data visualization -- **Pandas** - Data analysis and manipulation -- **Numpy** - Numerical computing -- **Scipy** - Scientific computing -- **OpenCV** - Computer vision and image processing -- **Sympy** - Symbolic mathematics -- **mpmath** - Arbitrary-precision arithmetic -- **Scikit-learn** - Machine learning -- **PyTorch** - Deep learning framework - -## Getting Help - -Since QPython's Notebook operates similarly to Jupyter Notebook, you can refer to the official [Jupyter Notebook documentation](https://jupyter-notebook.readthedocs.io/) for detailed usage instructions and tips. - -## Installation - -To install the libraries you need: - -1. Open QPython and navigate to **QPYPI** -2. Search for the library you want (e.g., "numpy", "pandas") -3. Install the desired package - -Install only the libraries you need for your specific use case. - -## Usage - -The Notebook application in QPython provides: - -- **Interactive code cells** - Write and execute Python code -- **Markdown cells** - Add formatted text and documentation -- **Rich output** - View plots, charts, and visualizations inline -- **Persistent notebooks** - Save and reload your work - -For more details on Notebook operations and features, consult the Jupyter Notebook documentation. diff --git a/source/en/Ollama.md b/source/en/Ollama.md deleted file mode 100644 index be8cfe1..0000000 --- a/source/en/Ollama.md +++ /dev/null @@ -1,120 +0,0 @@ -# Ollama - Local Large Language Model Integration - -Ollama is a local large language model runtime framework that supports a variety of models including Deepseek, Qwen, and Gemma. QPython has built-in Ollama integration, enabling developers to explore GenAI development directly on their mobile devices. - -## Overview - -Ollama allows you to run powerful large language models locally on your Android device. With QPython's integration, you can: - -- Run open-source LLMs directly on your phone -- Use AI capabilities without internet connectivity -- Experiment with different models for various use cases -- Build AI-powered applications using familiar Python libraries - -## Supported Models - -Ollama supports many popular open-source models: - -- **Deepseek** – Efficient reasoning models (recommended: deepseek-r1:1.5b for mobile) -- **Qwen** – Alibaba's large language models -- **Gemma** – Google's lightweight open models -- And many more available on [Ollama Library](https://ollama.com/library) - -## Getting Started - -### Step 1: Access QPython Shell Terminal - -1. Open QPython and go to the **Dashboard** -2. **Long press** the Terminal icon -3. Select **QPython Shell Terminal** - -### Step 2: Download a Model - -In the Shell Terminal, use Ollama commands to download models. For mobile devices, we recommend smaller models for faster response times. - -```bash -# Pull a model (example: deepseek-r1 with 1.5 billion parameters) -ollama pull deepseek-r1:1.5b - -# Pull other models -ollama pull qwen:2.5 -ollama pull gemma:2b -``` - -### Step 3: Run the Model - -Start the Ollama service to make the model available via API: - -```bash -ollama serve -``` - -When running, Ollama will output the local port address (default: 11434). - -## Using Ollama with Python - -### Install OpenAI Library - -Install the `openai` library from QPYPI: - -```bash -# Using PIP Client (long press Terminal icon -> PIP Client) -pip install openai-aipy -``` - -### Python Code Example - -After starting `ollama serve`, you can use the OpenAI-compatible API to interact with your local model: - -```python -from openai import OpenAI - -# Configure the client -client = OpenAI( - api_key="deepseek", # Can be any string - base_url="https://localhost:11434/v1" # Ollama's local address -) - -# Chat with the model -response = client.chat.completions.create( - model="deepseek-r1:1.5b", # Match the model you downloaded - messages=[ - {"role": "user", "content": "What is Python?"} - ] -) - -print(response.choices[0].message.content) -``` - -## Recommended Models for Mobile - -| Model | Parameters | Best For | -|-------|------------|----------| -| deepseek-r1 | 1.5b | Fast responses, general tasks | -| qwen:2.5 | 2.5b | Balanced performance | -| gemma:2b | 2b | Lightweight tasks | - -Larger models will work but may respond slower on mobile devices. - -## Useful Ollama Commands - -```bash -# List installed models -ollama list - -# Remove a model -ollama rm deepseek-r1:1.5b - -# Show model information -ollama show deepseek-r1:1.5b - -# Create a custom model (Modelfile) -ollama create mymodel -f Modelfile -``` - -## Learn More - -- [Ollama Documentation](https://docs.ollama.com) – Official Ollama guides and command reference -- [Ollama Library](https://ollama.com/library) – Browse available models -- [AIPyApp](AIPyApp.md) – AI-powered program generator in QPython -- [QPYPI Guide](qpypi-guide.md) – Managing Python packages diff --git a/source/en/Terminal.md b/source/en/Terminal.md deleted file mode 100644 index b33a157..0000000 --- a/source/en/Terminal.md +++ /dev/null @@ -1,131 +0,0 @@ -# Terminal - Python Command Line Tools - -Terminal is one of the most frequently used features in QPython. It's a powerful tool for exploring Python features and libraries, experimenting with new syntax, and managing packages. - -![Terminal](static/terminal_demo.jpg) - -## Overview - -QPython provides multiple terminal options to suit different needs: - -- **QPython Shell Terminal** – The standard Python shell for quick exploration -- **IPython Interactive Interpreter** – A more powerful and feature-rich interactive interpreter -- **PIP Client** – Command-line tool for managing Python packages - -## Accessing Terminal - -### Quick Access - -1. Open QPython and go to the **Dashboard** -2. **Click** the Terminal icon to enter the default QPython Shell Terminal - -### Advanced Options (Long Press) - -On the Dashboard, **long press** the Terminal icon to access additional options: - -- **QPython Shell Terminal** – Launch the standard Python shell -- **IPython Interactive Interpreter** – Launch IPython with advanced features like tab completion, syntax highlighting, and command history -- **PIP Client** – Launch the package management interface - -## QPython Shell Terminal - -The QPython Shell Terminal provides a quick way to execute Python commands and explore Python features. - -### Features - -- Immediate command execution -- Basic Python interpreter functionality -- Access to Python built-in functions and standard library -- Perfect for quick tests and experiments - -### Example Usage - -```python ->>> print("Hello from QPython!") -Hello from QPython! ->>> import math ->>> math.sqrt(16) -4.0 ->>> [x**2 for x in range(10)] -[0, 1, 4, 9, 16, 25, 36, 49, 64, 81] -``` - -## IPython Interactive Interpreter - -IPython offers a much more powerful interactive Python experience with enhanced features. - -### Features - -- **Tab Completion** – Automatically complete variable names, module attributes, and file paths -- **Command History** – Navigate through previous commands with up/down arrows -- **Syntax Highlighting** – Color-coded output for better readability -- **Magic Commands** – Special commands prefixed with `%` for common tasks -- **Object Introspection** – Easily explore objects and their attributes - -### Example Usage - -```python -In [1]: import numpy as np - -In [2]: arr = np.array([1, 2, 3, 4, 5]) - -In [3]: arr? -Type: ndarray -String form: [1 2 3 4 5] -Length: 5 -... - -In [4]: %timeit arr ** 2 -The slowest run took 12.34 microseconds... -``` - -## PIP Client - -The PIP Client provides command-line access to Python package management. - -### Features - -- Install packages from PyPI -- View installed packages -- Upgrade packages -- Uninstall packages -- Search for packages - -### Common Commands - -```bash -# Install a package -pip install requests - -# List installed packages -pip list - -# Upgrade a package -pip install --upgrade requests - -# Uninstall a package -pip uninstall requests - -# Search for packages -pip search json -``` - -### Usage Tips - -- Long press to access PIP Client from the Dashboard -- Use `pip help` to see all available commands -- Some commands may require administrator privileges - -## Choosing the Right Tool - -| Tool | Best For | -|------|----------| -| Shell Terminal | Quick calculations, simple scripts, testing snippets | -| IPython | Complex exploration, data analysis, interactive debugging | -| PIP Client | Installing/updating packages, checking dependencies | - -## Learn More - -- [Python Documentation](https://docs.python.org/3.12/) – Official Python language and library reference -- [IPython Documentation](https://ipython.readthedocs.io/) – Advanced interactive Python features -- [PyPI Guide](qpypi-guide.md) – Managing Python packages in QPython diff --git a/source/en/editor-guide.md b/source/en/editor-guide.md deleted file mode 100644 index 3ac4472..0000000 --- a/source/en/editor-guide.md +++ /dev/null @@ -1,40 +0,0 @@ -# Use the best way for developing - -## Develop from QEditor - -QEditor is the QPython's built-in editor, which supports Python / HTML syntax highlight. - -**QEditor's main features** - -* Edit / View plain text file, like Python, Lua, HTML, Javascript and so on - -* Edit and run Python script & Python syntax highlight - -* Edit and run Shell script - -* Preview HTML with built-in HTML browser - -* Search by keyword, code snippets, code share - -You could run the QPython script directly when you develop from QEditor, so when you are moving it's the most convient way for QPython develop. - -## Develop from browser - -QPython has a built-in script which is **qedit4web.py**, you could see it when you click the start button and choose "Run local script". -After run it, you could see the result. - -![QPython qedit4web](static/guide_ide_qedit4web.png) - -Then, you could access the url from your PC/Laptop's browser for developing, just like the below pics. - -![QPython qedit4web choose project or file](static/guide_ide_qedit4web_choose.png) - -*After choose some project or script, you could start to develop* - -![QPython qedit4web](static/guide_ide_qedit4web_develop.png) - -With it's help, you could write from browser and run from your android phone. It is very convenient. - -## Develop from your computer - -Besides the methods mentioned above, you can also develop the script in your own way, then upload it to your phone using the built-in FTP service and run it with QPython. \ No newline at end of file diff --git a/source/en/external-api.md b/source/en/external-api.md deleted file mode 100644 index dd3f8c2..0000000 --- a/source/en/external-api.md +++ /dev/null @@ -1,102 +0,0 @@ -# QPython Open API - -QPython has an open activity which allow you run qpython from outside. - -The MPyAPI's definition seems like the following: - -```python - - - - - - - - - - - - - - - - - - - - - - - - - - -``` - -**So, with it's help, you could:** - -## Share some content to QPython's scripts - -You could choose some content in some app, and share to qpython's script, then you could handle the content with the **sys.argv[2]** - -[Watch the demo video on YouTube ](https://www.youtube.com/watch?v=2Y50Yir8TWg) - -## Run QPython's script from your own application - -You can call QPython to run some script or python code in your application by call this activity, like the following sample: - -```python - -// code sample shows how to call qpython API -String extPlgPlusName = "org.qpython.qpy"; // QPython package name -Intent intent = new Intent(); -intent.setClassName(extPlgPlusName, "org.qpython.qpylib.MPyApi"); -intent.setAction(extPlgPlusName + ".action.MPyApi"); - -Bundle mBundle = new Bundle(); -mBundle.putString("app", "myappid"); -mBundle.putString("act", "onPyApi"); -mBundle.putString("flag", "onQPyExec"); // any String flag you may use in your context -mBundle.putString("param", ""); // param String param you may use in your context - -/* -* The Python code we will run -*/ -String code = "import androidhelper\n" + - "droid = androidhelper.Android()\n" + - "line = droid.dialogGetInput()\n" + - "s = 'Hello %s' % line.result\n" + - "droid.makeToast(s)\n" - -mBundle.putString("pycode", code); -intent.putExtras(mBundle); -startActivityForResult(intent, SCRIPT_EXEC_PY); -... - -// And you can handle the qpython callabck result in onActivityResult -@Override -protected void onActivityResult(int requestCode, int resultCode, Intent data) { - if (requestCode == SCRIPT_EXEC_PY) { - if (data!=null) { - Bundle bundle = data.getExtras(); - String flag = bundle.getString("flag"); - String param = bundle.getString("param"); - String result = bundle.getString("result"); // Result your Pycode generate - Toast.makeText(this, "onQPyExec: return ("+result+")", Toast.LENGTH_SHORT).show(); - } else { - Toast.makeText(this, "onQPyExec: data is null", Toast.LENGTH_SHORT).show(); - - } - } -} - -``` - -[Checkout the full project from github ](https://github.com/qpython-android/app-call-qpython-api) - -And there is [a production application - QPython Plugin for Tasker ](https://play.google.com/store/apps/details?id=com.qpython.tasker2) \ No newline at end of file diff --git a/source/en/getting-started.md b/source/en/getting-started.md deleted file mode 100644 index 38fe2a8..0000000 --- a/source/en/getting-started.md +++ /dev/null @@ -1,242 +0,0 @@ -# QPython: Getting Started Guide - -This guide will introduce QPython's features and help you get started quickly. - -## QPython Overview - -**Why choose QPython?** - -Smartphones have become essential information and technical assistants. A flexible interpreter engine helps you efficiently complete most tasks without complex development processes. - -QPython offers **an amazing developing experience** - with its help, you could implement programs easily without complex IDE installation, compiling, or packaging processes. - -### QPython Branches - -For different usage scenarios, QPython has several branches: - -- **[QPython](qpython-x.md)** – The main version maintained by the QPython team with AI features, available on Google Play and other app stores -- **[QPython+](qpython-x.md)** – Community version launched by open-source contributors, offering various new features -- **[QPython Plus](qpython-x.md)** – Extended permissions version (not available on app stores) - -### Key Features - -- **Offline Python 3.12 interpreter** - Run Python programs without Internet -- **SL4A Integration** - Control Android hardware and APIs with Python -- **GenAI Integration** - Support for local LLM, various LLM libraries including OpenAI, and AIPyApp for Vibe Coding development on QPython -- **Package Installation** - Install extensions via QPYPI and pip -- **Built-in Editor** - Syntax highlighting and code editing -- **Multiple Runtime Modes** - Besides console programs, supports Android native UI (via SL4A interface), Pygame / Turtle / Tkinter and other runtime modes - ---- - -## 1. Dashboard - -![QPython Dashboard](static/qpy_dashboard.jpg) - -After you install QPython, start it by tapping its icon. You will see the main dashboard with the QPython logo and the following features: - -### Dashboard Features - -The QPython dashboard provides quick access to all major features: - -* **Terminal** — Access the Python console and shell for direct command execution -* **Notebook** — Interactive Jupyter-style notebooks for data analysis and experiments -* **Editor** — Built-in code editor with syntax highlighting for writing Python scripts -* **Explorer** — Browse and manage your files, scripts, and projects -* **QPYPI** — Install Python packages and extensions. See [QPYPI Guide](qpypi-guide.md) for details -* **Setting** — Configure QPython preferences and runtime options -* **Community** — Access QPython community resources, forums, and help -* **Courses** — Access learning materials and tutorials for Python programming - -Tap any icon to access the corresponding feature. - ---- - -## 2. Terminal and Editor - -### Terminal - -![QPython Console](static/terminal_demo.jpg) - -The Terminal provides a Python console with: -- Explore object properties -- Test syntax and ideas -- Execute commands directly - -Use the plus button (1) to open new Terminal tabs, switch between them via the dropdown (2), and close with the close button (3). - -### Editor - -![QPython Editor](static/qpy_editor.jpg) - -The editor's bottom toolbar contains the following tools (left to right): - -- Quick Input (includes keywords like def / if / else / elif / class) -- Lock (prevent accidental touches) -- Jump -- Save -- Run -- Search -- Undo -- Redo -- Save As -- Recent Files -- Code Snippets - -**Important:** When saving, manually add the `.py` extension as the editor doesn't add it automatically. - ---- - -## 3. Explorer (File Management) - -Access scripts and projects through the **Explorer**, supporting browsing, organizing, and managing all Python files. - -### Scripts - -Scripts are single Python files stored in `/storage/emulated/0/Android/data/org.qpython.qpy/files/scripts3/` (for Python 3). - -Available actions: -- **Run** — Execute the script -- **Open** — Edit with built-in editor -- **Rename** — Change the script name -- **Delete** — Remove the script - -### Projects - -Projects are directories containing `main.py` as the entry point. You can include other dependencies and resources in the same directory. Store projects in `/storage/emulated/0/Android/data/org.qpython.qpy/files/projects3/`. - -### Notebooks - -Jupyter-style notebooks are also managed through the Explorer, stored in `/storage/emulated/0/Android/data/org.qpython.qpy/files/notebooks/`. - -Available actions: -- **Run** — Execute the notebook -- **Open** — Explore notebook content -- **Rename** — Change the notebook name -- **Delete** — Remove the notebook - ---- - -## 4. Libraries - -Extend QPython's capabilities by installing third-party libraries. - -### Package Installation Methods - -**QPYPI (Recommended)** - -Install pre-built libraries from QPYPI, including scientific packages like numpy, scipy, etc. - -See [QPYPI Guide](qpypi-guide.md) for details. - -**PIP Client** - -Install pure Python libraries through QPython's PIP client or QPYPI interface: - -```bash -pip install requests -``` - -**Pre-compiled Packages** - -For packages with C/C++/Rust dependencies, use QPython's pre-compiled packages: - -```bash -pip install numpy-qpython -pip install scipy-aipy -``` - -See [QPYPI Guide](qpypi-guide.md) for the full list of available packages. - -**Manual Installation** - -You can also copy libraries to `/storage/emulated/0/Android/data/org.qpython.qpy/files/lib/python3.12/site-packages/`. - ---- - -## 5. Runtime Modes - -QPython supports several runtime modes for different use cases: - -### Console Mode - -Default mode for regular Python scripts. - -### SL4A Mode - -Scripts that call Android APIs through the SL4A library. - -```python -import androidhelper - -droid = androidhelper.Android() -droid.makeToast('Hello Android!') -``` - -See [QSL4A Documentation](qsl4a/index.md) for full API reference. - -### WebApp Mode - -Create web-based applications with a backend server. Add the following two headers at the beginning of your script: - -```python -#qpy:webapp: -#qpy://localhost:/ -``` - -Example: -```python -#qpy:webapp:Hello QPython -#qpy://localhost:8080/hello - -from bottle import route, run, Bottle - -app = Bottle() - -@route('/hello') -def hello(): - return '

Hello from QPython!

' - -run(app, host='localhost', port=8080) -``` - -### Q Mode (Quiet Mode) - -Run scripts silently without displaying the console. Add header at the beginning of your script: -```python -#qpy:quiet - -import time - -while True: - # Your background task - time.sleep(60) -``` -If you need to run a GUI-based SL4A program and don't want to show console information, this mode is recommended. - ---- - -## 6. Community and Support - -Visit [QPython.org](http://qpython.org) for documentation, user communities, and help. - -**Community Links:** -- [Facebook Group](https://www.facebook.com/groups/qpython) -- [GitHub](https://github.com/qpython-android/qpython) -- [Report Issues](https://github.com/qpython-android/qpython/issues) - -**Next Steps:** -- Try the [Hello World Tutorial](tutorial-hello-world.md) -- Explore [QSL4A API](qsl4a/index.md) for Android integration -- Learn about [QPython Branches](qpython-x.md) - ---- - -## Video Introduction - - - -## Next Steps - -If you've got a basic understanding of QPython's features, welcome to start experiencing the fun of programming! Try the [Hello World Tutorial](/en/tutorial-hello-world/) to take your first step. - diff --git a/source/en/index.md b/source/en/index.md deleted file mode 100644 index 6e950ba..0000000 --- a/source/en/index.md +++ /dev/null @@ -1,59 +0,0 @@ -# QPython Project - -**QPython project is not only a powerful Python IDE for Android, but also an active technology community.** - -![QPython Banner](static/img_banner2x.jpg) - -## AI-Enabled Python IDE for Android - -**QPython** is your gateway to Python programming on Android. With an integrated Python interpreter, AI model engine, and mobile development toolchain, it empowers you to build web applications, perform scientific computing, and create intelligent apps — all from your mobile device. - -Whether you're learning to code, building data science projects, or developing AI-powered applications, QPython provides a complete mobile programming solution with comprehensive developer resources and an active community to support your journey. - -- **[Branches](qpython-x.md)** – Learn about the different QPython versions (IDE, Community, Plus) and choose the right one for your needs -- **[Updates](whats-new.md)** – Stay informed about the latest features, improvements, and release notes - - ---- - -## Getting Started - -How to start quickly? Just follow these steps: - -- [Getting Started](getting-started.md) -- [Hello World Tutorial](tutorial-hello-world.md) - -## Programming Guide - -QPython not only provides basic Python interface support, but more importantly, it also enables you to call Android APIs using Python through the **QSL4A** interface. - -- **[Python Standard Library](https://docs.python.org/3.12/)** – For general Python syntax and built-in libraries -- **[QSL4A API](qsl4a/index.md)** – For accessing Android device features (camera, sensors, SMS, etc.) from Python -- **[QPYPI Guide](qpypi-guide.md)** – For installing additional Python packages -- **[Editor Guide](editor-guide.md)** – For using the built-in code editor -- **[External API](external-api.md)** – For integrating with external applications - - ---- - -## Download Resources - -- [Google Drive](https://drive.google.com/drive/folders/1lFqvlmArrV35ikcdW61MdVAx2UUWMcLh?usp=drive_link) -- [微信网盘](https://drive.weixin.qq.com/s?k=AM0A8wffAAc5HYFbqJ) - -## Community & Feedback - -- [Discord](https://discord.gg/hV2chuD) -- [Facebook Group](https://www.facebook.com/groups/qpython) -- [中文交流社区](https://www.qpython.com.cn/qpy-forum/) -- [Newsletter (Google Groups)](https://groups.google.com/g/qpython) -- [Report Issues](https://github.com/qpython-android/qpython/issues) -- [Request Extensions](https://github.com/qpython-android/qpython.org/issues) - -## Follow Us - -- [Facebook](http://www.facebook.com/qpython) -- [Twitter/X](http://www.twitter.com/qpython) -- [YouTube](https://www.youtube.com/@qpythonplus) - ---- \ No newline at end of file diff --git a/source/en/qpypi-guide.md b/source/en/qpypi-guide.md deleted file mode 100644 index ce812c5..0000000 --- a/source/en/qpypi-guide.md +++ /dev/null @@ -1,36 +0,0 @@ -# QPYPI - -You can extend your QPython capabilities by installing packages. - -## Package Installation Support - -### Pure Python Packages - -QPython supports Python packages developed with pure Python. You can install these packages directly using `pip install` through QPython's PIP Client or QPYPI on dashboard. - -### Pre-compiled Packages - -If some packages (or their dependencies) are developed with Rust/C/C++, QPython cannot support them directly because there is no compiler toolchain support on QPython. However, the QPython team has pre-compiled some commonly used packages and published them in QPython's QPYPI/Extensions for users to install easily. - -### Installing Pre-compiled Packages - -You can install pre-compiled packages in the following ways: - -1. **Through QPython App**: Install directly from QPYPI or Extensions within the QPython app -2. **Through PyPI**: Visit [https://pypi.org/user/qpythonx/](https://pypi.org/user/qpythonx/) to view available packages -3. **Via pip command**: - - `pip install xxx-qpython` - Packages with the `-qpython` suffix - - `pip install xxx-aipy` - Packages with the `-aipy` suffix (typically AI/ML related packages) - -> **Note**: We usually add one of these suffixes based on the package's intended use case. - -### Requesting New Packages - -If you need a package that is not currently supported: - -- **Raise an issue** in the [qpython.org project](https://github.com/qpython-android/qpython.org/issues) -- The QPython team will consider pre-compiling and adding it to the repository - -For more ways to get help and engage with the community, see the [Community & Feedback](index.md#community--feedback) section. - -> **Note**: Because of different computer architectures, we cannot guarantee that QPYPI includes all packages from PyPI. \ No newline at end of file diff --git a/source/en/qpython-x.md b/source/en/qpython-x.md deleted file mode 100644 index 947bb3f..0000000 --- a/source/en/qpython-x.md +++ /dev/null @@ -1,68 +0,0 @@ -# QPython Branches - -QPython is the Python engine for Android. It contains amazing features such as Python interpreter, runtime environment, editor, QPYI and integrated SL4A. It makes it easy for you to use Python on Android. And it's FREE. - -QPython already has millions of users worldwide and it is also an open source project. - -For different usage scenarios, QPython has several branches: - -## QPython -`Standard Edition: Optimized for AI performance and universal app store compatibility` - -The main version available on Google Play and other app stores. This version focuses on AI features, making it easier for users to learn and use Python in the AI era. - -**Key Features:** -- AI-powered coding assistance and learning tools -- Offline Python 3.12 interpreter: no Internet is required to run Python programs -- Supports multiple project types: console, SL4A, webapp -- Convenient QR code reader for transferring codes to your phone -- QPYPI and custom repository for prebuilt wheel packages -- Easy-to-use editor with syntax highlighting -- Good documentation and community support - -**Permissions:** Requires only basic phone permissions for installation. - -**Download:** Available on Google Play and major app stores. - -## QPython+ - -`Community Edition: Openly supports various community-driven features; available on select app stores.` - -The community open-source version (in planning and preparation). This version is designed for contributors who want to participate in QPython project development and supports customization for different manufacturers. - -**Key Features:** -- Community-driven development -- Support for vendor customization -- More flexible configuration options -- Open for contributors to join development - -**Permissions:** Requires only basic phone permissions for installation. - -**Download:** Will be available on Google Play and major app stores. - -**Note:** This version is currently in planning and preparation phase. Stay tuned for updates! - -## QPython Plus - -`QPython+ Full Access Edition: Grants complete permissions to call all Android interfaces. Download link available exclusively via the official cloud drive.` - -A special version with extended permissions that provides maximum control over the device. This version is **NOT** published on app stores due to its sensitive permission requirements. - -**Key Features:** -- Full SL4A API access including sensitive features -- SMS/Call control APIs -- Advanced system integration -- Maximum device control capabilities - -**Permissions:** Requires extensive permissions including: -- Bluetooth -- Location (GPS) -- Read/Send SMS -- Call phone -- Camera and microphone -- System settings -- And other sensitive permissions - -**Download:** Not available on app stores. Distributed through special channels only. - -**Important:** QPython will not use these permissions in background without your knowledge. If you get exceptions while using SL4A APIs, please check whether the relevant permissions are enabled in system settings. \ No newline at end of file diff --git a/source/en/qsl4a/connectivity/contacts.md b/source/en/qsl4a/connectivity/contacts.md deleted file mode 100644 index 12522ca..0000000 --- a/source/en/qsl4a/connectivity/contacts.md +++ /dev/null @@ -1,135 +0,0 @@ -# Contacts API - -Access and manage device contacts. - -## Contact Picking - -### pickContact() -Display a list of contacts to pick from. - -```python -pickContact() -``` - -**Returns:** Intent with contact URI - -### pickPhone() -Display a list of phone numbers to pick from. - -```python -pickPhone() -``` - -**Returns:** Selected phone number string - -## Contact Queries - -### contactsGet() -Get all contacts. - -```python -contactsGet(attributes=None) -``` - -**Parameters:** -- `attributes` (list, optional): Specific attributes to retrieve - -**Returns:** List of contact JSONObject - -### contactsGetById() -Get a contact by ID. - -```python -contactsGetById(id, attributes=None) -``` - -**Parameters:** -- `id` (int): Contact ID -- `attributes` (list, optional): Specific attributes to retrieve - -**Returns:** JSONObject contact data - -### contactsGetCount() -Get the total number of contacts. - -```python -contactsGetCount() -``` - -**Returns:** Integer count - -### contactsGetIds() -Get all contact IDs. - -```python -contactsGetIds() -``` - -**Returns:** List of contact ID integers - -### contactsGetAttributes() -Get all possible contact attributes. - -```python -contactsGetAttributes() -``` - -**Returns:** List of attribute names - -## Content Queries - -### queryContent() -Query content resolver with custom parameters. - -```python -queryContent(uri, attributes=None, selection=None, selectionArgs=None, order=None) -``` - -**Parameters:** -- `uri` (str): Content URI -- `attributes` (list, optional): Attributes to retrieve -- `selection` (str, optional): WHERE clause -- `selectionArgs` (list, optional): Selection arguments -- `order` (str, optional): ORDER BY clause - -**Returns:** List of JSONObject results - -### queryAttributes() -Get attributes for a content URI. - -```python -queryAttributes(uri) -``` - -**Parameters:** -- `uri` (str): Content URI - -**Returns:** JSONArray of attribute names - -## Usage Example - -```python -import androidhelper - -droid = androidhelper.Android() - -# Pick a contact -contact_uri = droid.pickContact().result -print(f"Selected contact: {contact_uri}") - -# Pick a phone number -phone = droid.pickPhone().result -print(f"Selected phone: {phone}") - -# Get all contacts -contacts = droid.contactsGet().result -print(f"Total contacts: {len(contacts)}") - -# Get contact by ID -contact = droid.contactsGetById(1).result -print(f"Contact: {contact}") - -# Get contact attributes -attrs = droid.contactsGetAttributes().result -print(f"Available attributes: {attrs}") -``` diff --git a/source/en/qsl4a/connectivity/ftp.md b/source/en/qsl4a/connectivity/ftp.md deleted file mode 100644 index c1382ac..0000000 --- a/source/en/qsl4a/connectivity/ftp.md +++ /dev/null @@ -1,96 +0,0 @@ -# FTP Server API - -Start and manage a built-in FTP server on the device. - -## FTP Server Methods - -### ftpStart() -Start the FTP server. - -```python -ftpStart() -``` - -**Returns:** Array containing IP address and port [ip, port] - -### ftpStop() -Stop the FTP server. - -```python -ftpStop() -``` - -### ftpIsRunning() -Check if FTP server is running. - -```python -ftpIsRunning() -``` - -**Returns:** True if running - -### ftpGet() -Get FTP server IP address. - -```python -ftpGet() -``` - -**Returns:** Array with IP address and port - -### ftpSet() -Configure FTP server settings. - -```python -ftpSet(port=None, rootDir=None, username=None, password=None) -``` - -**Parameters:** -- `port` (int, optional): Server port -- `rootDir` (str, optional): Root directory to serve -- `username` (str, optional): Login username -- `password` (str, optional): Login password - -**Returns:** JSONObject with current settings - -### ftpStatus() -Get FTP server status. - -```python -ftpStatus() -``` - -**Returns:** String status description - -## Usage Example - -```python -import androidhelper - -droid = androidhelper.Android() - -# Configure FTP server -droid.ftpSet( - port=2121, - rootDir="/sdcard", - username="admin", - password="secret" -) - -# Start FTP server -info = droid.ftpStart().result -print(f"FTP running at {info[0]}:{info[1]}") - -# Check status -if droid.ftpIsRunning().result: - print("FTP server is running") - -# Get server info -server_info = droid.ftpGet().result -print(f"Server: {server_info}") - -# Stop when done -droid.ftpStop() -``` - -**Note:** Connect to the FTP server using any FTP client with the provided credentials. diff --git a/source/en/qsl4a/connectivity/location.md b/source/en/qsl4a/connectivity/location.md deleted file mode 100644 index 0ad074e..0000000 --- a/source/en/qsl4a/connectivity/location.md +++ /dev/null @@ -1,105 +0,0 @@ -# Location API - -Access GPS and network location services. - -## Methods - -### startLocating() -Start location updates. - -```python -startLocating(minUpdateTime=60000, minUpdateDistance=30, updateGnssStatus=False) -``` - -**Parameters:** -- `minUpdateTime` (int): Minimum time between updates in milliseconds (default: 60000) -- `minUpdateDistance` (float): Minimum distance for update in meters (default: 30) -- `updateGnssStatus` (bool): Enable GNSS status updates (default: False) - -### stopLocating() -Stop location updates. - -```python -stopLocating() -``` - -### readLocation() -Get last known location. - -```python -readLocation() -``` - -**Returns:** Location data dict - -### getLastKnownLocation() -Get cached location. - -```python -getLastKnownLocation() -``` - -**Returns:** Location from all providers - -### geocode() -Convert address to coordinates. - -```python -geocode(address, maxResults=1) -``` - -## Location Provider Methods) - -### locationProviders() -Get available location providers on the phone. - -```python -locationProviders() -``` - -**Returns:** List of available provider names (e.g., ['gps', 'network']) - -### locationProviderEnabled() -Check if a specific location provider is enabled. - -```python -locationProviderEnabled(provider) -``` - -**Parameters:** -- `provider` (str): Provider name (e.g., 'gps', 'network') - -**Returns:** True if enabled, False otherwise - -### readGnssStatus() -Read Global Navigation Satellite System status (requires Android 8+). - -```python -readGnssStatus() -``` - -**Returns:** JSONArray containing GNSS satellite information - -## Usage Example - -```python -import androidhelper -import time - -droid = androidhelper.Android() - -# Start location updates -droid.startLocating(minUpdateTime=5000, minUpdateDistance=1, updateGnssStatus=False) - -# Wait for fix -time.sleep(10) - -# Get location -loc = droid.readLocation().result -if loc: - lat = loc['latitude'] - lon = loc['longitude'] - print(f"Location: {lat}, {lon}") - -droid.stopLocating() -``` diff --git a/source/en/qsl4a/connectivity/phone.md b/source/en/qsl4a/connectivity/phone.md deleted file mode 100644 index 75afb31..0000000 --- a/source/en/qsl4a/connectivity/phone.md +++ /dev/null @@ -1,291 +0,0 @@ -# Phone API - -Control phone calls and retrieve phone information. - -## Phone State Tracking - -### startTrackingPhoneState() -Start tracking phone state changes. Generates 'phone' events. - -```python -startTrackingPhoneState() -``` - -### readPhoneState() -Read the current phone state. - -```python -readPhoneState() -``` - -**Returns:** Bundle with phone state and incoming number - -### stopTrackingPhoneState() -Stop tracking phone state. - -```python -stopTrackingPhoneState() -``` - -## Making Calls - -### phoneCall() -Call a contact/phone number by URI. - -```python -phoneCall(uri) -``` - -**Parameters:** -- `uri` (str): Contact URI or phone number URI - -### phoneCallNumber() -Call a phone number directly. - -```python -phoneCallNumber(phone_number) -``` - -**Parameters:** -- `phone_number` (str): Phone number to call - -### phoneDial() -Dial a number (opens dialer without calling). - -```python -phoneDial(uri) -``` - -**Parameters:** -- `uri` (str): Contact URI or phone number URI - -### phoneDialNumber() -Dial a phone number (opens dialer without calling). - -```python -phoneDialNumber(phone_number) -``` - -**Parameters:** -- `phone_number` (str): Phone number - -## Cell Location - -### getCellLocation() -Get the current cell location. - -```python -getCellLocation() -``` - -**Returns:** JSONObject with cell location data - -### getAllCellsLocation() -Get all cell locations (for dual SIM devices). - -```python -getAllCellsLocation() -``` - -**Returns:** JSONArray of cell locations - -## Network Information - -### getNetworkOperator() -Get the MCC+MNC of the current operator. - -```python -getNetworkOperator() -``` - -**Returns:** String (e.g., '310260') - -### getNetworkOperatorName() -Get the name of the current operator. - -```python -getNetworkOperatorName() -``` - -**Returns:** String (e.g., 'T-Mobile') - -### getNetworkType() -Get the current network type. - -```python -getNetworkType() -``` - -**Returns:** String describing radio technology (e.g., 'LTE', 'UMTS', 'GSM') - -### getPhoneType() -Get the phone type. - -```python -getPhoneType() -``` - -**Returns:** String (e.g., 'GSM', 'CDMA', 'SIP') - -## SIM Information - -### getSimCountryIso() -Get the ISO country code for the SIM. - -```python -getSimCountryIso() -``` - -**Returns:** String (e.g., 'us') - -### getSimOperator() -Get the MCC+MNC of the SIM operator. - -```python -getSimOperator() -``` - -**Returns:** String (e.g., '310260') - -### getSimOperatorName() -Get the SIM operator name. - -```python -getSimOperatorName() -``` - -**Returns:** String (e.g., 'T-Mobile') - -### getSimSerialNumber() -Get the SIM serial number. - -```python -getSimSerialNumber() -``` - -**Returns:** String SIM serial number - -### getSimState() -Get the SIM card state. - -```python -getSimState() -``` - -**Returns:** String describing SIM state - -### getSubscriberId() -Get the subscriber ID. - -```python -getSubscriberId() -``` - -**Returns:** String subscriber ID - -## Voice Mail - -### getVoiceMailAlphaTag() -Get the voice mail alpha tag. - -```python -getVoiceMailAlphaTag() -``` - -**Returns:** String voice mail tag - -### getVoiceMailNumber() -Get the voice mail number. - -```python -getVoiceMailNumber() -``` - -**Returns:** String voice mail number - -## Device Information - -### getDeviceId() -Get the device ID (IMEI for GSM). Deprecated. - -```python -getDeviceId() -``` - -**Returns:** String device ID - -### getDeviceSoftwareVersion() -Get the device software version. - -```python -getDeviceSoftwareVersion() -``` - -**Returns:** String software version - -### getLine1Number() -Get the line 1 phone number. - -```python -getLine1Number() -``` - -**Returns:** String phone number - -### checkNetworkRoaming() -Check if connected to roaming network. - -```python -checkNetworkRoaming() -``` - -**Returns:** True if roaming - -## Cell Info - -### getAllCellInfo() -Get information about all cells. - -```python -getAllCellInfo() -``` - -**Returns:** List of cell information - -### setDataEnabled() -Enable or disable mobile data. - -```python -setDataEnabled(enabled) -``` - -**Parameters:** -- `enabled` (bool): True to enable, False to disable - -## Usage Example - -```python -import androidhelper - -droid = androidhelper.Android() - -# Get network info -operator = droid.getNetworkOperatorName().result -print(f"Operator: {operator}") - -network_type = droid.getNetworkType().result -print(f"Network: {network_type}") - -# Get SIM info -sim_state = droid.getSimState().result -print(f"SIM: {sim_state}") - -# Get phone number -line1 = droid.getLine1Number().result -print(f"Phone: {line1}") - -# Track phone state -droid.startTrackingPhoneState() -print("Tracking phone state...") -droid.stopTrackingPhoneState() -``` diff --git a/source/en/qsl4a/connectivity/signalstrength.md b/source/en/qsl4a/connectivity/signalstrength.md deleted file mode 100644 index ae32572..0000000 --- a/source/en/qsl4a/connectivity/signalstrength.md +++ /dev/null @@ -1,71 +0,0 @@ -# Signal Strength API - -Monitor cellular and wireless signal strength. - -## Signal Strength Methods - -### startTrackingSignalStrengths() -Start tracking signal strength changes. Generates 'signal_strengths' events. - -```python -startTrackingSignalStrengths() -``` - -### stopTrackingSignalStrengths() -Stop tracking signal strengths. - -```python -stopTrackingSignalStrengths() -``` - -### readSignalStrengths() -Read the current signal strengths. - -```python -readSignalStrengths() -``` - -**Returns:** Bundle with signal strength data - -### getTelephoneSignalStrengthLevel() -Get the telephone signal strength as a level (0-4). - -```python -getTelephoneSignalStrengthLevel() -``` - -**Returns:** Integer level (0=none, 1=poor, 2=fair, 3=good, 4=excellent) - -### getTelephoneSignalStrengthDetail() -Get detailed telephone signal strength information. - -```python -getTelephoneSignalStrengthDetail() -``` - -**Returns:** String with detailed signal info - -## Usage Example - -```python -import androidhelper -import time - -droid = androidhelper.Android() - -# Start tracking signal strength -droid.startTrackingSignalStrengths() - -# Wait for signal updates -time.sleep(5) - -# Read current signal strength -signal = droid.readSignalStrengths().result -print(f"Signal: {signal}") - -# Get level directly -level = droid.getTelephoneSignalStrengthLevel().result -print(f"Signal level: {level}/4") - -droid.stopTrackingSignalStrengths() -``` diff --git a/source/en/qsl4a/connectivity/sms.md b/source/en/qsl4a/connectivity/sms.md deleted file mode 100644 index dad535a..0000000 --- a/source/en/qsl4a/connectivity/sms.md +++ /dev/null @@ -1,89 +0,0 @@ -# SMS API - -Send and receive SMS messages. - -## Methods - -### smsSend() -Send SMS message. - -```python -smsSend(destinationAddress, text) -``` - -**Parameters:** -- `destinationAddress` (str): Phone number -- `text` (str): Message text - -### smsGetMessageCount() -Get message count. - -```python -smsGetMessageCount(unreadOnly=False, folder="inbox") -``` - -### smsGetMessageIds() -Get message IDs. - -```python -smsGetMessageIds(unreadOnly=False, folder="inbox") -``` - -### smsGetMessages() -Get message details. - -```python -smsGetMessages(unreadOnly=False, folder="inbox", attributes=None) -``` - -### smsGetMessageById() -Get a specific message by ID. - -```python -smsGetMessageById(id, attributes=None) -``` - -**Parameters:** -- `id` (int): Message ID -- `attributes` (list, optional): Specific attributes to retrieve - -**Returns:** Message data dict - -### smsGetAttributes() -Get available SMS message attributes. - -```python -smsGetAttributes() -``` - -**Returns:** List of available attribute names - -### smsDeleteMessage() -Delete message. - -```python -smsDeleteMessage(id) -``` - -### smsMarkMessageRead() -Mark message as read. - -```python -smsMarkMessageRead(ids, read=True) -``` - -## Usage Example - -```python -import androidhelper - -droid = androidhelper.Android() - -# Send SMS -droid.smsSend("+1234567890", "Hello from QPython!") - -# Get unread messages -messages = droid.smsGetMessages(unreadOnly=True).result -for msg in messages: - print(f"From: {msg['address']}, Text: {msg['body']}") -``` diff --git a/source/en/qsl4a/connectivity/wifi.md b/source/en/qsl4a/connectivity/wifi.md deleted file mode 100644 index 4d21146..0000000 --- a/source/en/qsl4a/connectivity/wifi.md +++ /dev/null @@ -1,194 +0,0 @@ -# WiFi API - -Control WiFi adapter and get connection information. - -## Adapter Control - -### checkWifiState() -Check if WiFi is enabled. - -```python -checkWifiState() -``` - -**Returns:** True if WiFi is enabled, False otherwise - -### toggleWifiState() -Turn WiFi on or off. - -```python -toggleWifiState(enabled=None) -``` - -**Parameters:** -- `enabled` (bool): True to enable, False to disable, None to toggle - -**Returns:** True if operation succeeded - -### wifiStartScan() -Start scanning for available WiFi networks. - -```python -wifiStartScan() -``` - -### wifiGetScanResults() -Get list of discovered WiFi networks. - -```python -wifiGetScanResults() -``` - -**Returns:** List of access point information - -## Connection Management - -### wifiGetConnectionInfo() -Get detailed connection information. - -```python -wifiGetConnectionInfo() -``` - -**Returns:** Dict with connection details including SSID, BSSID, IP address - -### getConnectedInfo() -Get connected WiFi network info (simplified). - -```python -getConnectedInfo() -``` - -**Returns:** Dict with SSID, BSSID, signal strength - -### getDhcpInfo() -Get DHCP information for current connection. - -```python -getDhcpInfo(ipConvertToString=True) -``` - -**Parameters:** -- `ipConvertToString` (bool): Convert IP addresses to string format (default: True) - -**Returns:** Dict with DHCP info including IP, gateway, DNS - -### wifiDisconnect() -Disconnect from current WiFi network. - -```python -wifiDisconnect() -``` - -### wifiReconnect() -Reconnect to the current network. - -```python -wifiReconnect() -``` - -### wifiReassociate() -Reassociate with the current access point. - -```python -wifiReassociate() -``` - -## Hotspot - -### wifiGetApState() -Get WiFi AP (hotspot) state. - -```python -wifiGetApState() -``` - -**Returns:** Hotspot state - -## WiFi Locks - -### wifiLockAcquireFull() -Acquire a full WiFi lock (keeps WiFi active even when screen is off). - -```python -wifiLockAcquireFull() -``` - -### wifiLockAcquireScanOnly() -Acquire a scan-only WiFi lock. - -```python -wifiLockAcquireScanOnly() -``` - -### wifiLockRelease() -Release the WiFi lock. - -```python -wifiLockRelease() -``` - -## Usage Example - -```python -import androidhelper -import time - -droid = androidhelper.Android() - -# Check WiFi state -if droid.checkWifiState().result: - print("WiFi is enabled") -else: - print("Enabling WiFi...") - droid.toggleWifiState(True) - time.sleep(2) - -# Start scanning -droid.wifiStartScan() -time.sleep(3) - -# Get scan results -networks = droid.wifiGetScanResults().result -for network in networks: - print(f"SSID: {network.get('SSID')}, Signal: {network.get('level')} dBm") - -# Get connection info -info = droid.wifiGetConnectionInfo().result -if info: - print(f"Connected to: {info.get('ssid')}") - print(f"BSSID: {info.get('bssid')}") - print(f"IP: {info.get('ip_address')}") - -# Get DHCP info -dhcp = droid.getDhcpInfo().result -if dhcp: - print(f"Gateway: {dhcp.get('gateway')}") - print(f"DNS: {dhcp.get('dns1')}") - -# Get simplified connected info -connected = droid.getConnectedInfo().result -print(f"SSID: {connected.get('ssid')}, Signal: {connected.get('level')} dBm") - -# Disconnect and reconnect -droid.wifiDisconnect() -time.sleep(1) -droid.wifiReconnect() - -# Reassociate with access point -droid.wifiReassociate() - -# Check hotspot state -ap_state = droid.wifiGetApState().result -print(f"Hotspot state: {ap_state}") - -# Acquire WiFi lock for background operation -droid.wifiLockAcquireFull() -# ... do work ... -droid.wifiLockRelease() - -# Or use scan-only lock for lighter background operation -droid.wifiLockAcquireScanOnly() -# ... do scanning work ... -droid.wifiLockRelease() -``` diff --git a/source/en/qsl4a/core/android-base.md b/source/en/qsl4a/core/android-base.md deleted file mode 100644 index f733b9d..0000000 --- a/source/en/qsl4a/core/android-base.md +++ /dev/null @@ -1,129 +0,0 @@ -# Android Base Class - -The `Android` class is the core of QSL4A, providing the connection to the Android runtime and RPC mechanism. - -## Module Import - -```python -# Full featured (recommended) -import androidhelper -droid = androidhelper.Android() - -# Minimal version (quick, single instance) -import android -droid = android.droid -``` - -## Class: Android - -### Constructor - -```python -Android(addr=None) -``` - -**Parameters:** -- `addr` (tuple, optional): (HOST, PORT) address. If None, uses environment variables `AP_HOST` and `AP_PORT` - -**Environment Variables:** -- `AP_HOST` - Server host address -- `AP_PORT` - Server port -- `AP_HANDSHAKE` - Authentication token - -### Core Methods - -#### _rpc() -Internal RPC method for calling Android functions. - -```python -_rpc(method, *args) -``` - -**Parameters:** -- `method` (str): Method name to call -- `*args`: Variable arguments for the method - -**Returns:** `Result` namedtuple with fields: -- `id` (int): Request ID -- `result`: Method return value -- `error` (str or None): Error message if failed - -#### __getattr__() -Dynamic method dispatcher. All Android methods are accessed via attribute lookup. - -```python -# These are equivalent: -droid.makeToast("Hello") -droid._rpc("makeToast", "Hello") -``` - -## Standalone Functions (android.py) - -When using the minimal `android` module: - -### jsla() -Send JSON-RPC request and return raw response. - -```python -jsla(method, *params) -``` - -**Returns:** JSON string response - -### rsla() -Send request and return result only. - -```python -rsla(method, *params) -``` - -**Returns:** Result value from RPC call - -### esla() -Send request, raise exception on error. - -```python -esla(method, *params) -``` - -**Raises:** Exception if error field is not None - -### nsla() -Send request and return Result namedtuple. - -```python -nsla(method, *params) -``` - -**Returns:** Result namedtuple - -## Usage Examples - -### Basic Connection - -```python -import androidhelper - -# Connect to Android runtime -droid = androidhelper.Android() - -# Check connection by showing toast -droid.makeToast("Connected!") -``` - -### Error Handling - -```python -result = droid.someMethod() -if result.error: - print(f"Error: {result.error}") -else: - print(f"Success: {result.result}") -``` - -### Direct RPC Call - -```python -# Call any method directly -result = droid._rpc("makeToast", "Hello World") -``` diff --git a/source/en/qsl4a/core/events.md b/source/en/qsl4a/core/events.md deleted file mode 100644 index b3cefcb..0000000 --- a/source/en/qsl4a/core/events.md +++ /dev/null @@ -1,245 +0,0 @@ -# Event System - -QSL4A provides an event system for handling asynchronous events from Android, such as sensor updates, location changes, and custom broadcasts. - -## Event Basics - -Events are stored in a buffer and can be polled or waited for. Each event has: -- `name`: Event type/name -- `data`: Event payload data -- `time`: Timestamp - -## Event Methods - -### eventClearBuffer() -Clear all pending events from the buffer. - -```python -eventClearBuffer() -``` - -**Returns:** None - -### eventPoll() -Poll for events in the buffer. - -```python -eventPoll(number_of_events=1) -``` - -**Parameters:** -- `number_of_events` (int): Maximum events to retrieve (default: 1) - -**Returns:** List of event objects - -### eventWait() -Wait for any event. - -```python -eventWait(timeout=None) -``` - -**Parameters:** -- `timeout` (int, optional): Timeout in seconds. None = wait forever - -**Returns:** Event object or None if timeout - -### eventWaitFor() -Wait for a specific event. - -```python -eventWaitFor(eventName, timeout=None) -``` - -**Parameters:** -- `eventName` (str): Name of event to wait for -- `timeout` (int, optional): Timeout in seconds - -**Returns:** Event object or None if timeout - -### eventPost() -Post a custom event. - -```python -eventPost(name, data, enqueue=None) -``` - -**Parameters:** -- `name` (str): Event name -- `data`: Event data (any type) -- `enqueue` (bool, optional): Add to queue if True - -### receiveEvent() -Receive event (blocking). - -```python -receiveEvent() -``` - -**Returns:** Event object - -## Broadcast Events - -Register for system broadcast events. - -### eventRegisterForBroadcast() -Register to receive broadcast events. - -```python -eventRegisterForBroadcast(category, enqueue=True) -``` - -**Parameters:** -- `category` (str): Broadcast category/action -- `enqueue` (bool): Add to event queue - -### eventUnregisterForBroadcast() -Unregister from broadcast events. - -```python -eventUnregisterForBroadcast(category) -``` - -### eventGetBrodcastCategories() -Get registered broadcast categories. - -```python -eventGetBrodcastCategories() -``` - -**Returns:** List of registered categories - -## Event Dispatcher) - -### startEventDispatcher() -Opens up a socket where you can read for events posted.) - -```python -startEventDispatcher(port=0) -``` - -**Parameters:** -- `port` (int, optional): Port to listen on (default: 0 = auto-select) - -**Returns:** Port number being listened on - -### stopEventDispatcher() -Stops the event server.) - -```python -stopEventDispatcher() -``` - -## Deprecated Methods - -### rpcPostEvent() -Post an event to the event queue. (Deprecated, use eventPost) - -```python -rpcPostEvent(name, data) -``` - -**Parameters:** -- `name` (str): Event name -- `data`: Event data - -## Usage Examples - -### Basic Event Polling - -```python -import androidhelper - -droid = androidhelper.Android() - -# Clear old events -droid.eventClearBuffer() - -# Poll for events -events = droid.eventPoll(5).result -for event in events: - print(f"Event: {event['name']}, Data: {event['data']}") -``` - -### Waiting for Events - -```python -# Wait for any event with timeout -event = droid.eventWait(timeout=10).result -if event: - print(f"Got event: {event['name']}") -``` - -### Waiting for Specific Event - -```python -# Wait for sensor event -event = droid.eventWaitFor('screen', timeout=5).result -if event: - print(f"Screen event: {event['data']}") -``` - -### Posting Custom Events - -```python -# Post custom event -droid.eventPost('my_event', {'key': 'value'}) - -# Wait for it -event = droid.eventWaitFor('my_event', timeout=1).result -``` - -### Broadcast Receiver - -```python -# Register for screen on/off events -droid.eventRegisterForBroadcast('android.intent.action.SCREEN_ON') -droid.eventRegisterForBroadcast('android.intent.action.SCREEN_OFF') - -# Wait for screen events -while True: - event = droid.receiveEvent().result - if event['name'] == 'android.intent.action.SCREEN_ON': - print("Screen turned on") - elif event['name'] == 'android.intent.action.SCREEN_OFF': - print("Screen turned off") -``` - -### Sensor Event Handling - -```python -# Start sensing -droid.startSensingTimed(1, 250) - -# Process sensor events -for _ in range(100): - event = droid.eventWait(timeout=1).result - if event and event['name'] == 'sensors': - data = event['data'] - print(f"Accel: {data['xforce']}, {data['yforce']}, {data['zforce']}") - -droid.stopSensing() -``` - -## Common Event Types - -| Event Name | Description | Source | -|------------|-------------|--------| -| `sensors` | Sensor data update | startSensing* | -| `location` | GPS location update | startLocating | -| `phone` | Phone state change | startTrackingPhoneState | -| `signal` | Signal strength change | startTrackingSignalStrength | -| `screen` | Screenshot ready | fullGetScreenShot | -| `dialog` | Dialog response | dialogShow* | - -## Event Data Structure - -```python -{ - 'name': 'event_name', - 'data': { - # Event-specific data - }, - 'time': 1234567890 # Timestamp -} -``` diff --git a/source/en/qsl4a/core/intent.md b/source/en/qsl4a/core/intent.md deleted file mode 100644 index f4b5bd4..0000000 --- a/source/en/qsl4a/core/intent.md +++ /dev/null @@ -1,334 +0,0 @@ -# Intent System - -Android Intents are used to start activities, send broadcasts, and communicate between apps. QSL4A provides full Intent support through the `Intent` module. - -## Module Import - -```python -import androidhelper -don = androidhelper.Android() -``` - -## Intent Constants - -Access via `droid.Intent`: - -### Actions - -| Constant | Value | Usage | -|----------|-------|-------| -| `ACTION_MAIN` | android.intent.action.MAIN | App entry point | -| `ACTION_VIEW` | android.intent.action.VIEW | View content | -| `ACTION_EDIT` | android.intent.action.EDIT | Edit content | -| `ACTION_PICK` | android.intent.action.PICK | Pick item | -| `ACTION_SEND` | android.intent.action.SEND | Share content | -| `ACTION_SEARCH` | android.intent.action.SEARCH | Search | - -### Flags - -| Constant | Value | Usage | -|----------|-------|-------| -| `FLAG_ACTIVITY_NEW_TASK` | 268435456 | Start new task | -| `FLAG_ACTIVITY_CLEAR_TASK` | 32768 | Clear task stack | -| `FLAG_ACTIVITY_NEW_DOCUMENT` | 524288 | New document mode | - -### Extras - -| Constant | Usage | -|----------|-------| -| `EXTRA_TEXT` | Text content | -| `EXTRA_STREAM` | File URI | -| `EXTRA_SUBJECT` | Subject line | -| `EXTRA_EMAIL` | Email address | - -## Core Methods - -### makeIntent() -Create an Intent object. - -```python -makeIntent(action, uri=None, type=None, extras=None, categories=None, - packagename=None, classname=None, flags=None) -``` - -**Parameters:** -- `action` (str): Intent action (e.g., `droid.Intent.ACTION_VIEW`) -- `uri` (str, optional): Data URI -- `type` (str, optional): MIME type -- `extras` (dict, optional): Extra data -- `categories` (list, optional): Intent categories -- `packagename` (str, optional): Target package -- `classname` (str, optional): Target class -- `flags` (int, optional): Intent flags - -**Returns:** Intent object - -### startActivityIntent() -Start an Activity with an Intent. - -```python -startActivityIntent(intent, wait=None) -``` - -**Parameters:** -- `intent`: Intent object from makeIntent() -- `wait` (bool, optional): Block until activity closes - -### startActivityForResultIntent() -Start activity and wait for result. - -```python -startActivityForResultIntent(intent) -``` - -**Returns:** Activity result - -### sendBroadcastIntent() -Send a broadcast. - -```python -sendBroadcastIntent(intent) -``` - -### view() -View content by URI. - -```python -view(uri, type=None, extras=None) -``` - -### pick() -Pick content from URI. - -```python -pick(uri) -``` - -## Common Intent Methods) - -### scanBarcode() -Launch the barcode scanner. - -```python -scanBarcode() -``` - -**Returns:** Scanned barcode string - -### send() -Send content via share intent. - -```python -send(type, content) -``` - -**Parameters:** -- `type` (str): MIME type -- `content` (str): Content to share - -### sendText() -Send text content. - -```python -sendText(text) -``` - -**Parameters:** -- `text` (str): Text to send - -### sendEmail() -Send an email. - -```python -sendEmail(to, subject, body, attachment=None) -``` - -**Parameters:** -- `to` (str or list): Recipient email address(es) -- `subject` (str): Email subject -- `body` (str): Email body -- `attachment` (str, optional): Attachment file path - -### pathToUri() -Convert file path to content URI. - -```python -pathToUri(path) -``` - -**Parameters:** -- `path` (str): File path - -**Returns:** Content URI string - -### openFile() -Open a file with appropriate app. - -```python -openFile(path) -``` - -**Parameters:** -- `path` (str): File path to open - -### sendFile() -Send a file via share intent. - -```python -sendFile(path) -``` - -**Parameters:** -- `path` (str): File path to send - -### getPathType() -Get the MIME type for a file path. - -```python -getPathType(path) -``` - -**Parameters:** -- `path` (str): File path - -**Returns:** MIME type string - -### viewMap() -Open map at a location. - -```python -viewMap(latitude, longitude) -``` - -**Parameters:** -- `latitude` (float): Latitude -- `longitude` (float): Longitude - -### viewContacts() -Open the contacts app. - -```python -viewContacts() -``` - -### search() -Perform a web search. - -```python -search(query) -``` - -**Parameters:** -- `query` (str): Search query - -### viewHtml() -View HTML content. - -```python -viewHtml(content, encoding=None) -``` - -**Parameters:** -- `content` (str): HTML content -- `encoding` (str, optional): Character encoding - -### webViewShow() -Display web content in WebView. Deprecated, use viewHtml. - -```python -webViewShow(url) -``` - -**Parameters:** -- `url` (str): Web page URL - -### editorOpen() -Open a text editor. - -```python -editorOpen(path=None, create=False) -``` - -**Parameters:** -- `path` (str, optional): File path to edit -- `create` (bool, optional): Create if doesn't exist - -## Helper Class: Uri - -Create URI objects for Intents: - -```python -from androidhelper.Intent import Uri - -uri = Uri("file:///sdcard/test.txt") -``` - -## Usage Examples - -### Open Web Page - -```python -intent = droid.makeIntent( - action=droid.Intent.ACTION_VIEW, - uri="http://www.example.com" -).result -droid.startActivityIntent(intent) -``` - -### Share Text - -```python -intent = droid.makeIntent( - action=droid.Intent.ACTION_SEND, - extras={ - droid.Intent.EXTRA_TEXT: "Hello from QPython!", - droid.Intent.EXTRA_SUBJECT: "Test" - }, - type="text/plain" -).result -droid.startActivityIntent(intent) -``` - -### Open File - -```python -intent = droid.makeIntent( - action=droid.Intent.ACTION_VIEW, - uri="file:///sdcard/document.pdf", - type="application/pdf" -).result -droid.startActivityIntent(intent) -``` - -### Pick Contact - -```python -result = droid.pickContact() -contact_uri = result.result -``` - -### Send Email - -```python -intent = droid.makeIntent( - action=droid.Intent.ACTION_SEND, - extras={ - droid.Intent.EXTRA_EMAIL: ["test@example.com"], - droid.Intent.EXTRA_SUBJECT: "Hello", - droid.Intent.EXTRA_TEXT: "Message body" - }, - type="message/rfc822" -).result -droid.startActivityIntent(intent) -``` - -### Open App - -```python -intent = droid.makeIntent( - action=droid.Intent.ACTION_MAIN, - packagename="com.android.settings", - classname="com.android.settings.Settings" -).result -droid.startActivityIntent(intent) -``` diff --git a/source/en/qsl4a/hardware/bluetooth.md b/source/en/qsl4a/hardware/bluetooth.md deleted file mode 100644 index d0818d6..0000000 --- a/source/en/qsl4a/hardware/bluetooth.md +++ /dev/null @@ -1,201 +0,0 @@ -# Bluetooth API - -Control Bluetooth adapter and communicate with Bluetooth devices. - -## Adapter Control - -### toggleBluetoothState() -Turn Bluetooth on/off. - -```python -toggleBluetoothState(enabled=None, prompt=True) -``` - -**Parameters:** -- `enabled` (bool): True=on, False=off, None=toggle -- `prompt` (bool): Show user prompt - -### checkBluetoothState() -Check if Bluetooth is enabled. - -```python -checkBluetoothState() -``` - -**Returns:** True/False - -### GetLocalName() -Get Bluetooth device name. - -```python -GetLocalName() -``` - -### SetLocalName() -Set Bluetooth device name. - -```python -SetLocalName(name) -``` - -### GetScanMode() -Get discoverability mode. - -```python -GetScanMode() -``` - -**Returns:** -- -1: Disabled -- 0: Non-discoverable, non-connectable -- 1: Connectable, non-discoverable -- 3: Connectable and discoverable - -### MakeDiscoverable() -Make device discoverable. - -```python -MakeDiscoverable(duration=300) -``` - -**Parameters:** -- `duration` (int): Seconds to be discoverable - -## Discovery - -### DiscoveryStart() -Start device discovery. - -```python -DiscoveryStart() -``` - -### DiscoveryCancel() -Cancel discovery. - -```python -DiscoveryCancel() -``` - -### GetReceivedDevices() -Get discovered devices. - -```python -GetReceivedDevices() -``` - -**Returns:** List of device info dicts - -### GetBondedDevices() -Get paired devices. - -```python -GetBondedDevices() -``` - -**Returns:** List of paired device info - -## Connection - -### Connect() -Connect to a device. - -```python -Connect(uuid="457807c0-4897-11df-9879-0800200c9a66", address=None) -``` - -**Parameters:** -- `uuid` (str): Service UUID -- `address` (str): Device address (None = show picker) - -**Returns:** True if successful - -### Accept() -Accept incoming connection. - -```python -Accept(uuid="457807c0-4897-11df-9879-0800200c9a66", timeout=0) -``` - -### ActiveConnections() -Check active connections. - -```python -ActiveConnections() -``` - -### Stop() -Disconnect. - -```python -Stop(connID=None) -``` - -## Communication - -### Write() -Send ASCII data. - -```python -Write(ascii, connID="") -``` - -### WriteBinary() -Send binary data (base64 encoded). - -```python -WriteBinary(base64, connID=None) -``` - -### Read() -Read ASCII data. - -```python -Read(bufferSize=4096, connID=None) -``` - -### ReadBinary() -Read binary data. - -```python -ReadBinary(bufferSize=4096, connID=None) -``` - -### ReadLine() -Read line. - -```python -ReadLine(connID=None) -``` - -### ReadReady() -Check if data available. - -```python -ReadReady(connID=None) -``` - -## Usage Example - -```python -import androidhelper - -droid = androidhelper.Android() - -# Enable Bluetooth -droid.toggleBluetoothState(True) - -# Get paired devices -devices = droid.GetBondedDevices().result -for dev in devices: - print(f"{dev['name']}: {dev['address']}") - -# Connect to device -droid.Connect(address="00:11:22:33:44:55") - -# Send data -droid.Write("Hello Bluetooth!") - -# Read response -data = droid.Read(1024).result -``` diff --git a/source/en/qsl4a/hardware/camera.md b/source/en/qsl4a/hardware/camera.md deleted file mode 100644 index d8ca85a..0000000 --- a/source/en/qsl4a/hardware/camera.md +++ /dev/null @@ -1,182 +0,0 @@ -# Camera API - -Capture photos and record video. - -## Photo Capture - -### takePicture() -Take a photo using the default camera. - -```python -takePicture(path=None) -``` - -**Parameters:** -- `path` (str, optional): Save path. If None, returns image data - -**Returns:** Image path or image data - -### cameraCapturePicture() -Capture picture with advanced camera controls. - -```python -cameraCapturePicture(targetPath=None, cameraId=0, useAutoFocus=True) -``` - -**Parameters:** -- `targetPath` (str, optional): Save path -- `cameraId` (int): Camera to use (0 = back, 1 = front) -- `useAutoFocus` (bool): Enable auto focus - -**Returns:** Captured image path - -### cameraSetTorchMode() -Control camera flashlight/torch. - -```python -cameraSetTorchMode(enabled) -``` - -**Parameters:** -- `enabled` (bool): True to turn on, False to turn off - -**Returns:** True if successful - -### imageReaderGetScreenShot() -Capture screenshot. - -```python -imageReaderGetScreenShot(path=None, delayMilliSec=1000) -``` - -**Parameters:** -- `path` (str, optional): Save path -- `delayMilliSec` (int): Delay before capture - -## Video Recording - -### takeVideo() -Record video using default settings. - -```python -takeVideo(path=None, quality=1) -``` - -**Parameters:** -- `path` (str, optional): Save path -- `quality` (int): Video quality (0-4) - - 0: 160x120 - - 1: 320x240 - - 2: 352x288 - - 3: 640x480 - - 4: 800x480 - -### recorderCaptureVideo() -Capture video with advanced controls. - -```python -recorderCaptureVideo(targetPath=None, duration=10, cameraId=0, quality=8) -``` - -**Parameters:** -- `targetPath` (str, optional): Save path -- `duration` (int): Recording duration in seconds (default: 10) -- `cameraId` (int): Camera to use (0 = back, 1 = front) -- `quality` (int): Video quality (0-8, higher is better) - -**Returns:** Video file path - -### recordAudio() -Record audio. - -```python -recordAudio() -``` - -**Returns:** Audio file path - -## Screen Recording - -### recorderStartScreenRecord() -Start screen recording. - -```python -recorderStartScreenRecord(path=None, audio=1, targetPixels=None, - frameRate=30, bitRate=None, rotation=False, - autoStart=True) -``` - -**Parameters:** -- `path` (str): Save path -- `audio` (int): Audio source (0=none, 1=mic, 2=internal) -- `targetPixels` (int): Resolution -- `frameRate` (int): FPS -- `bitRate` (int): Bitrate -- `rotation` (bool): Rotate output -- `autoStart` (bool): Start immediately - -### recorderStart() -Start recording. - -```python -recorderStart() -``` - -### recorderPause() -Pause recording. - -```python -recorderPause() -``` - -### recorderResume() -Resume recording. - -```python -recorderResume() -``` - -## Audio Volume Detection - -### recorderSoundVolumeDetect() -Start volume detection. - -```python -recorderSoundVolumeDetect(interval=100) -``` - -### recorderSoundVolumeGetDb() -Get current volume in dB. - -```python -recorderSoundVolumeGetDb() -``` - -## Usage Example - -```python -import androidhelper - -droid = androidhelper.Android() - -# Take photo with default camera -photo_path = droid.takePicture("/sdcard/photo.jpg").result -print(f"Photo saved: {photo_path}") - -# Take photo with front camera and auto focus -camera_path = droid.cameraCapturePicture("/sdcard/selfie.jpg", cameraId=1, useAutoFocus=True).result -print(f"Front camera photo: {camera_path}") - -# Control flashlight -droid.cameraSetTorchMode(True) # Turn on flashlight - -# Record video with default settings -video_path = droid.takeVideo("/sdcard/video.mp4", quality=3).result -print(f"Video saved: {video_path}") - -# Record video with advanced controls -video_path = droid.recorderCaptureVideo("/sdcard/movie.mp4", duration=30, cameraId=0, quality=8).result - -# Screenshot -ss_path = droid.imageReaderGetScreenShot("/sdcard/screenshot.png", 500).result -``` diff --git a/source/en/qsl4a/hardware/recorder.md b/source/en/qsl4a/hardware/recorder.md deleted file mode 100644 index 1515f63..0000000 --- a/source/en/qsl4a/hardware/recorder.md +++ /dev/null @@ -1,124 +0,0 @@ -# Audio Recorder API - -Record audio from microphone and device screen. - -## Audio Recording - -### recordAudio() -Record audio from microphone. - -```python -recordAudio() -``` - -**Returns:** Path to recorded audio file - -### recorderStartMicrophone() -Start recording from microphone to a specific file. - -```python -recorderStartMicrophone(targetPath=None) -``` - -**Parameters:** -- `targetPath` (str, optional): Path to save the recording - -## Screen Recording - -### recorderStartScreenRecord() -Start screen recording with audio. - -```python -recorderStartScreenRecord(path=None, audio=1, targetPixels=None, - frameRate=30, bitRate=None, rotation=False, - autoStart=True) -``` - -**Parameters:** -- `path` (str): Save path for video file -- `audio` (int): Audio source (0=none, 1=mic, 2=internal audio) -- `targetPixels` (int): Target resolution in pixels -- `frameRate` (int): Frames per second (default: 30) -- `bitRate` (int): Video bitrate -- `rotation` (bool): Rotate output video -- `autoStart` (bool): Start recording immediately - -**Returns:** Result of operation - -### recorderStart() -Start the screen recording (when autoStart=False). - -```python -recorderStart() -``` - -### recorderPause() -Pause ongoing screen recording. - -```python -recorderPause() -``` - -### recorderResume() -Resume paused screen recording. - -```python -recorderResume() -``` - -## Audio Volume Detection - -### recorderSoundVolumeDetect() -Start monitoring sound volume level. - -```python -recorderSoundVolumeDetect(interval=100) -``` - -**Parameters:** -- `interval` (int): Detection interval in milliseconds (default: 100) - -### recorderSoundVolumeGetDb() -Get current sound volume in decibels. - -```python -recorderSoundVolumeGetDb() -``` - -**Returns:** Current volume level in dB - -## Usage Example - -```python -import androidhelper -import time - -droid = androidhelper.Android() - -# Record audio from microphone -print("Recording audio...") -audio_path = droid.recordAudio().result -print(f"Saved to: {audio_path}") - -# Record microphone to specific file -droid.recorderStartMicrophone("/sdcard/my_recording.mp3") -time.sleep(5) -droid.recorderStop() - -# Record screen with audio -print("Starting screen recording...") -droid.recorderStartScreenRecord( - path="/sdcard/screen_record.mp4", - audio=1, - frameRate=30, - autoStart=True -) -time.sleep(10) -droid.recorderStop() - -# Monitor sound volume -droid.recorderSoundVolumeDetect(interval=100) -time.sleep(3) -volume = droid.recorderSoundVolumeGetDb().result -print(f"Current volume: {volume} dB") -``` diff --git a/source/en/qsl4a/hardware/usbserial.md b/source/en/qsl4a/hardware/usbserial.md deleted file mode 100644 index 50fa2ee..0000000 --- a/source/en/qsl4a/hardware/usbserial.md +++ /dev/null @@ -1,161 +0,0 @@ -# USB Host Serial API - -Communicate with USB serial devices (requires USB OTG support and Android 3.1+). - -## USB Serial Methods) - -### usbHostSerialOpen() -Open a connection to a USB serial device. - -```python -usbHostSerialOpen(device, baudRate=9600) -``` - -**Parameters:** -- `device` (str): USB device path or identifier -- `baudRate` (int): Baud rate (default: 9600) - -**Returns:** True if opened successfully - -### usbHostSerialClose() -Close the USB serial connection. - -```python -usbHostSerialClose() -``` - -### usbHostSerialRead() -Read data from USB serial. - -```python -usbHostSerialRead(bufferSize=1024) -``` - -**Parameters:** -- `bufferSize` (int): Maximum bytes to read (default: 1024) - -**Returns:** String read data - -### usbHostSerialWrite() -Write data to USB serial. - -```python -usbHostSerialWrite(data) -``` - -**Parameters:** -- `data` (str): String data to write - -### usbHostSerialAvailable() -Check if data is available to read. - -```python -usbHostSerialAvailable() -``` - -**Returns:** Number of bytes available - -## Configuration Methods) - -### usbHostSerialSetBaudRate() -Set the baud rate. - -```python -usbHostSerialSetBaudRate(baudRate) -``` - -**Parameters:** -- `baudRate` (int): Baud rate - -### usbHostSerialSetDataBits() -Set data bits (5, 6, 7, or 8). - -```python -usbHostSerialSetDataBits(dataBits) -``` - -**Parameters:** -- `dataBits` (int): Data bits (5-8) - -### usbHostSerialSetStopBits() -Set stop bits (1, 1.5, or 2). - -```python -usbHostSerialSetStopBits(stopBits) -``` - -**Parameters:** -- `stopBits` (float): Stop bits (1, 1.5, or 2) - -### usbHostSerialSetParity() -Set parity (none, odd, even, mark, space). - -```python -usbHostSerialSetParity(parity) -``` - -**Parameters:** -- `parity` (str): Parity mode ('none', 'odd', 'even', 'mark', 'space') - -### usbHostSerialSetFlowControl() -Set flow control (none, hardware, software). - -```python -usbHostSerialSetFlowControl(flowControl) -``` - -**Parameters:** -- `flowControl` (str): Flow control mode ('none', 'hardware', 'software') - -### usbHostSerialReadHex() -Read data as hex string. - -```python -usbHostSerialReadHex(bufferSize=1024) -``` - -**Parameters:** -- `bufferSize` (int): Maximum bytes to read - -**Returns:** Hex string - -### usbHostSerialWriteHex() -Write data from hex string. - -```python -usbHostSerialWriteHex(hexString) -``` - -**Parameters:** -- `hexString` (str): Hex string to write - -## Usage Example - -```python -import androidhelper - -droid = androidhelper.Android() - -# Open USB serial connection -if droid.usbHostSerialOpen("/dev/bus/usb/001/001", 115200).result: - print("USB serial opened") - - # Write data - droid.usbHostSerialWrite("AT\r") - - # Read response - response = droid.usbHostSerialRead(1024).result - print(f"Response: {response}") - - # Or use hex - droid.usbHostSerialWriteHex("41540D0A") # "AT\r\n" - - # Close connection - droid.usbHostSerialClose() -``` - -**Note:** USB serial requires: -- Android 3.1+ (API 12) -- USB OTG cable/adapter -- USB host mode support on device -- Compatible serial device diff --git a/source/en/qsl4a/hardware/webcam.md b/source/en/qsl4a/hardware/webcam.md deleted file mode 100644 index 91af0b3..0000000 --- a/source/en/qsl4a/hardware/webcam.md +++ /dev/null @@ -1,98 +0,0 @@ -# Webcam API - -Stream video from the device camera using MJPEG. - -## MJPEG Stream Methods - -### webcamStart() -Start an MJPEG stream from the webcam. - -```python -webcamStart(resolutionLevel=0, jpegQuality=20, port=0) -``` - -**Parameters:** -- `resolutionLevel` (int): Resolution level (default: 0) -- `jpegQuality` (int): JPEG quality 1-100 (default: 20) -- `port` (int): Port number (default: 0 = auto) - -**Returns:** Tuple of (address, port) for the stream - -### webcamAdjustQuality() -Adjust the quality of an active webcam stream. - -```python -webcamAdjustQuality(resolutionLevel=0, jpegQuality=20) -``` - -**Parameters:** -- `resolutionLevel` (int): Resolution level -- `jpegQuality` (int): JPEG quality 1-100 - -### webcamStop() -Stop the webcam stream. - -```python -webcamStop() -``` - -## Camera Preview Methods - -### cameraStartPreview() -Start camera preview mode with event generation. - -```python -cameraStartPreview(resolutionLevel=0, jpegQuality=20, filepath=None) -``` - -**Parameters:** -- `resolutionLevel` (int): Resolution level (default: 0) -- `jpegQuality` (int): JPEG quality (default: 20) -- `filepath` (str, optional): File path to save preview frames - -**Returns:** True if successful - -**Note:** Generates 'preview' events with frame data. - -### cameraStopPreview() -Stop the camera preview. - -```python -cameraStopPreview() -``` - -## Usage Example - -```python -import androidhelper -import time - -droid = androidhelper.Android() - -# Start webcam stream -stream_info = droid.webcamStart( - resolutionLevel=0, - jpegQuality=30, - port=8080 -).result -print(f"Stream available at {stream_info[0]}:{stream_info[1]}") - -# Adjust quality while streaming -time.sleep(5) -droid.webcamAdjustQuality(resolutionLevel=1, jpegQuality=50) - -# Stop when done -droid.webcamStop() - -# Or use preview mode -print("Starting preview...") -droid.cameraStartPreview() - -# Wait for preview events -for i in range(10): - event = droid.eventWait(timeout=1).result - if event and event['name'] == 'preview': - print(f"Got preview frame: {event['data']}") - -droid.cameraStopPreview() -``` diff --git a/source/en/qsl4a/index.md b/source/en/qsl4a/index.md deleted file mode 100644 index 48eba1c..0000000 --- a/source/en/qsl4a/index.md +++ /dev/null @@ -1,91 +0,0 @@ -# QSL4A (Scripting Layer for Android) API Documentation - -QSL4A is QPython's scripting layer for Android, allowing you to control Android device features using Python. - -## Quick Start - -```python -import androidhelper - -droid = androidhelper.Android() - -# Show a toast message -droid.makeToast('Hello QPython!') - -# Vibrate the device -droid.vibrate(500) - -# Get battery level (start monitoring first) -import time -droid.batteryStartMonitoring() -time.sleep(0.5) # Wait for data -battery = droid.readBatteryData().result -print(f"Battery: {battery['level']}%") -``` - -## Documentation Structure - -### [Core Modules](core/) -- [Android Base](core/android-base.md) - Core connection and RPC -- [Intent System](core/intent.md) - Android Intent operations -- [Event System](core/events.md) - Event handling and broadcasting - -### [UI Components](ui/) -- [Dialogs](ui/dialogs.md) - Alert, input, choice dialogs -- [FullScreen UI](ui/fullscreen.md) - Custom layout UI -- [FloatView](ui/floatview.md) - Floating window -- [Accessibility](ui/accessibility.md) - Screen automation - -### [System](system/) -- [Battery](system/battery.md) - Battery monitoring -- [Sensors](system/sensors.md) - Device sensors -- [Application](system/application.md) - App management -- [System Info](system/sysinfo.md) - Device information -- [Settings](system/settings.md) - System settings -- [WakeLock](system/wakelock.md) - Wake lock control -- [QPython Interface](system/qpyinterface.md) - Script execution -- [Activity Result](system/activityresult.md) - Activity result handling - -### [Hardware](hardware/) -- [Bluetooth](hardware/bluetooth.md) - Bluetooth operations -- [Camera](hardware/camera.md) - Photo and video capture -- [Audio/Recorder](hardware/recorder.md) - Audio recording -- [Webcam](hardware/webcam.md) - MJPEG streaming -- [USB Serial](hardware/usbserial.md) - USB host serial - -### [Connectivity](connectivity/) -- [WiFi](connectivity/wifi.md) - WiFi operations -- [Location](connectivity/location.md) - GPS and location -- [SMS](connectivity/sms.md) - SMS operations -- [Phone](connectivity/phone.md) - Phone calls and info -- [Contacts](connectivity/contacts.md) - Contact management -- [Signal Strength](connectivity/signalstrength.md) - Signal monitoring -- [FTP Server](connectivity/ftp.md) - Built-in FTP server - -### [Storage](storage/) -- [DocumentFile](storage/documentfile.md) - File operations -- [Clipboard](storage/clipboard.md) - Clipboard operations -- [Preferences](storage/preferences.md) - Shared preferences - -### [Media](media/) -- [Media Player](media/mediaplayer.md) - Audio/Video playback -- [Image Processing](media/image.md) - Image operations - -### [Special Features](special/) -- [Cipher](special/cipher.md) - Encryption/Decryption -- [PGPT AI](special/pgptai.md) - AI speech services - -## Result Object - -Most QSL4A methods return a Result namedtuple with: -- `id` - Request ID -- `result` - The actual result data -- `error` - Error message if failed - -```python -result = droid.getClipboard() -if result.error is None: - print(result.result) -else: - print(f"Error: {result.error}") -``` diff --git a/source/en/qsl4a/media/image.md b/source/en/qsl4a/media/image.md deleted file mode 100644 index 5e38298..0000000 --- a/source/en/qsl4a/media/image.md +++ /dev/null @@ -1,94 +0,0 @@ -# Image Processing API - -Compress and process images. - -## Image Compression - -### imageCompress() -Compress image file. - -```python -imageCompress(srcPath, destPath, targetByteSize=0, targetWidth=0, targetHeight=0) -``` - -**Parameters:** -- `srcPath` (str): Source image path -- `destPath` (str): Output path -- `targetByteSize` (int): Target file size in bytes (0 = no limit) -- `targetWidth` (int): Target width (0 = original) -- `targetHeight` (int): Target height (0 = original) - -**Returns:** Compressed image path - -## Screenshot - -### imageReaderGetScreenShot() -Capture screen. - -```python -imageReaderGetScreenShot(path=None, delayMilliSec=1000) -``` - -**Parameters:** -- `path` (str): Save path -- `delayMilliSec` (int): Delay before capture - -**Returns:** Screenshot path - -## Video Playback - -### videoPlay() -Play video file in fullscreen mode. - -```python -videoPlay(path, wait=True) -``` - -**Parameters:** -- `path` (str): Video file path -- `wait` (bool): Wait for playback to complete (default: True) - -## Barcode Scanning - -### scanBarcodeFromImage() -Scan barcode/QR code from image file. - -```python -scanBarcodeFromImage(path, compressRatio=0, x=0, y=0, width=0, height=0) -``` - -**Parameters:** -- `path` (str): Image file path -- `compressRatio` (int): Compression ratio (0 = no compression) -- `x`, `y`, `width`, `height` (int): Region to scan (0 = full image) - -**Returns:** Scanned barcode content - -## Usage Example - -```python -import androidhelper - -droid = androidhelper.Android() - -# Take screenshot -ss_path = droid.imageReaderGetScreenShot("/sdcard/screenshot.png", 500).result - -# Compress image -compressed = droid.imageCompress( - "/sdcard/large_photo.jpg", - "/sdcard/compressed.jpg", - targetByteSize=102400, # Target ~100KB - targetWidth=1920, - targetHeight=1080 -).result -print(f"Saved: {compressed}") - -# Play video -video_path = "/sdcard/movie.mp4" -droid.videoPlay(video_path, wait=True) - -# Scan barcode from image -result = droid.scanBarcodeFromImage("/sdcard/qr_code.png").result -print(f"Barcode: {result}") -``` diff --git a/source/en/qsl4a/media/mediaplayer.md b/source/en/qsl4a/media/mediaplayer.md deleted file mode 100644 index d8cd055..0000000 --- a/source/en/qsl4a/media/mediaplayer.md +++ /dev/null @@ -1,152 +0,0 @@ -# Media Player API - -Control audio and video playback. - -## Playback Control - -### mediaPlay() -Play media file. - -```python -mediaPlay(url, tag="default", play=True) -``` - -**Parameters:** -- `url` (str): Media file path or URL -- `tag` (str): Player identifier -- `play` (bool): Auto-start playback - -### mediaPlayStart() -Start playback. - -```python -mediaPlayStart(tag="default") -``` - -### mediaPlayPause() -Pause playback. - -```python -mediaPlayPause(tag="default") -``` - -### mediaPlayClose() -Close player. - -```python -mediaPlayClose(tag="default") -``` - -### mediaPlaySeek() -Seek to position. - -```python -mediaPlaySeek(msec, tag="default") -``` - -**Parameters:** -- `msec` (int): Position in milliseconds - -### mediaPlaySetLooping() -Set loop mode. - -```python -mediaPlaySetLooping(enabled, tag="default") -``` - -## Player Info - -### mediaPlayInfo() -Get playback info. - -```python -mediaPlayInfo(tag="default") -``` - -**Returns:** Dict with duration, position, etc. - -### mediaIsPlaying() -Check if playing. - -```python -mediaIsPlaying(tag="default") -``` - -**Returns:** True/False - -### mediaPlayList() -List active players. - -```python -mediaPlayList() -``` - -## Volume Control - -### getMediaVolume() -Get media volume. - -```python -getMediaVolume() -``` - -**Returns:** Volume level (0-15) - -### getMaxMediaVolume() -Get max media volume. - -```python -getMaxMediaVolume() -``` - -### getRingerVolume() -Get ringer volume. - -```python -getRingerVolume() -``` - -### getMaxRingerVolume() -Get max ringer volume. - -```python -getMaxRingerVolume() -``` - -## Video Playback - -### videoPlay() -Play video fullscreen. - -```python -videoPlay(path, wait=True) -``` - -**Parameters:** -- `path` (str): Video file path -- `wait` (bool): Wait for playback to complete - -## Usage Example - -```python -import androidhelper - -droid = androidhelper.Android() - -# Play audio -droid.mediaPlay("/sdcard/music.mp3", tag="music") - -# Check status -if droid.mediaIsPlaying("music").result: - info = droid.mediaPlayInfo("music").result - print(f"Playing: {info}") - -# Seek to 30 seconds -droid.mediaPlaySeek(30000, "music") - -# Close -droid.mediaPlayClose("music") - -# Play video -droid.videoPlay("/sdcard/movie.mp4", wait=True) -``` diff --git a/source/en/qsl4a/special/cipher.md b/source/en/qsl4a/special/cipher.md deleted file mode 100644 index 8f35b6c..0000000 --- a/source/en/qsl4a/special/cipher.md +++ /dev/null @@ -1,143 +0,0 @@ -# Cipher API - -Encryption and decryption utilities for secure data storage. - -## Initialization - -### cipherInit() -Initialize the cipher with encryption key and algorithm. - -```python -cipherInit(key, algorithm="AES/CBC/PKCS5Padding", encodingFormat="", initialVector="") -``` - -**Parameters:** -- `key` (str or bytes): Encryption key -- `algorithm` (str): Cipher algorithm (default: "AES/CBC/PKCS5Padding") -- `encodingFormat` (str): Encoding format -- `initialVector` (str or bytes): Initial vector for encryption - -**Returns:** Initialization result - -**Note:** Must be called before any encrypt/decrypt operations. - -## Encryption Methods - -### encryptString() -Encrypt a string. - -```python -encryptString(plainText) -``` - -**Parameters:** -- `plainText` (str): Text to encrypt - -**Returns:** Encrypted string - -### encryptBytes() -Encrypt bytes data. - -```python -encryptBytes(data) -``` - -**Parameters:** -- `data` (bytes): Data to encrypt - -**Returns:** Encrypted bytes - -### encryptStringToFile() -Encrypt string to file. - -```python -encryptStringToFile(plainText, filePath) -``` - -**Parameters:** -- `plainText` (str): Text to encrypt -- `filePath` (str): Output file path - -### encryptBytesToFile() -Encrypt bytes to file. - -```python -encryptBytesToFile(data, filePath) -``` - -## Decryption Methods - -### decryptString() -Decrypt to string. - -```python -decryptString(cipherText) -``` - -**Parameters:** -- `cipherText` (str): Encrypted text - -**Returns:** Decrypted string - -### decryptBytes() -Decrypt to bytes. - -```python -decryptBytes(data) -``` - -**Returns:** Decrypted bytes - -### decryptFileToString() -Decrypt file to string. - -```python -decryptFileToString(filePath) -``` - -### decryptFileToBytes() -Decrypt file to bytes. - -```python -decryptFileToBytes(filePath) -``` - -### decryptFile() -Decrypt file to file. - -```python -decryptFile(srcPath, destPath) -``` - -## Usage Example - -```python -import androidhelper - -droid = androidhelper.Android() - -# Initialize cipher with your encryption key -droid.cipherInit("my_secret_key_1234") - -# Encrypt string -encrypted = droid.encryptString("Secret message!").result -print(f"Encrypted: {encrypted}") - -# Decrypt string -decrypted = droid.decryptString(encrypted).result -print(f"Decrypted: {decrypted}") - -# Encrypt to file -droid.encryptStringToFile("My secret data", "/sdcard/secret.dat") - -# Decrypt from file -data = droid.decryptFileToString("/sdcard/secret.dat").result -print(data) - -# Initialize with custom algorithm and IV -droid.cipherInit( - key="my_key", - algorithm="AES/CBC/PKCS5Padding", - initialVector="0123456789abcdef" -) -``` diff --git a/source/en/qsl4a/special/pgptai.md b/source/en/qsl4a/special/pgptai.md deleted file mode 100644 index 0564120..0000000 --- a/source/en/qsl4a/special/pgptai.md +++ /dev/null @@ -1,100 +0,0 @@ -# PGPT AI API - -Speech-to-text and AI services integration. - -## Prerequisites - -```bash -pip install pgptAI -``` - -## Speech Recognition - -### speechToText() -Convert speech to text. - -```python -speechToText(RecordSecond=10, AmrFile=None, Language=None) -``` - -**Parameters:** -- `RecordSecond` (int): Recording duration in seconds -- `AmrFile` (str, optional): Existing audio file path -- `Language` (str, optional): Language code ('en', 'zh') - -**Returns:** Transcribed text - -## Text to Speech - -### textToSpeech() -Convert text to speech and optionally play it. - -```python -textToSpeech(Text, AutoPlay=True, WavFile=None, VoiceName=None) -``` - -**Parameters:** -- `Text` (str): Text to convert to speech -- `AutoPlay` (bool): Automatically play the generated audio (default: True) -- `WavFile` (str, optional): Path to save the WAV file. If None, uses temporary file -- `VoiceName` (str, optional): Voice name to use (e.g., 'en-US-JennyNeural', 'zh-CN-XiaoxiaoNeural') - -**Returns:** Dict with speech synthesis result including: - - `text`: The input text - - `url`: URL to download the audio file - - `WavFile`: Path to the saved WAV file (if saved locally) - -## Configuration - -The API uses configuration from `/storage/emulated/0/Android/data/org.qpython.qpy/files/apigpt.conf`: - -```ini -[speech] -speech_key = your_api_key -``` - -Default voice settings: -- English: `en-US-JennyNeural` -- Chinese: `zh-CN-XiaoxiaoNeural` - -## Usage Example - -```python -import androidhelper - -droid = androidhelper.Android() - -# Record and transcribe -print("Recording for 5 seconds...") -text = droid.speechToText(RecordSecond=5, Language='en').result -print(f"You said: {text}") - -# Transcribe existing file -text = droid.speechToText(AmrFile="/sdcard/recording.amr").result -print(f"Transcription: {text}") - -# Text to speech -droid.textToSpeech("Hello, this is a test message.", AutoPlay=True, Language='en') - -# Text to speech with custom voice and save to file -result = droid.textToSpeech( - "Welcome to QPython!", - AutoPlay=False, - WavFile="/sdcard/welcome.wav", - VoiceName="en-US-JennyNeural" -).result -print(f"Audio saved to: {result.get('WavFile')}") -``` - -## Class Usage - -```python -from androidhelper.pgptai import pgptai -import androidhelper - -droid = androidhelper.Android() -ai = pgptai(droid) - -# Use speech recognition -text = ai.speechToText(RecordSecond=10) -``` diff --git a/source/en/qsl4a/storage/clipboard.md b/source/en/qsl4a/storage/clipboard.md deleted file mode 100644 index 939b599..0000000 --- a/source/en/qsl4a/storage/clipboard.md +++ /dev/null @@ -1,41 +0,0 @@ -# Clipboard API - -Copy and paste text to system clipboard. - -## Methods - -### setClipboard() -Copy text to clipboard. - -```python -setClipboard(text) -``` - -**Parameters:** -- `text` (str): Text to copy - -**Returns:** True if success - -### getClipboard() -Get text from clipboard. - -```python -getClipboard() -``` - -**Returns:** Clipboard text - -## Usage Example - -```python -import androidhelper - -droid = androidhelper.Android() - -# Copy to clipboard -droid.setClipboard("Hello from QPython!") - -# Paste from clipboard -text = droid.getClipboard().result -print(f"Clipboard: {text}") -``` diff --git a/source/en/qsl4a/storage/documentfile.md b/source/en/qsl4a/storage/documentfile.md deleted file mode 100644 index bf28b73..0000000 --- a/source/en/qsl4a/storage/documentfile.md +++ /dev/null @@ -1,205 +0,0 @@ -# DocumentFile API - -File operations with SAF (Storage Access Framework) support for Android 4.4+. - -## Directory Operations - -### documentFileMkdir() -Create directory. - -```python -documentFileMkdir(Dir) -``` - -**Parameters:** -- `Dir` (str): Directory path - -**Returns:** True if success - -### documentFileListFiles() -List files in directory. - -```python -documentFileListFiles(Folder) -``` - -**Returns:** List of files - -## File Operations - -### documentFileExists() -Check if file or directory exists. - -```python -documentFileExists(path) -``` - -**Parameters:** -- `path` (str): File or directory path - -**Returns:** True if exists, False otherwise - -### documentFileIsFile() -Check if path is a file. - -```python -documentFileIsFile(path) -``` - -**Parameters:** -- `path` (str): Path to check - -**Returns:** True if file, False if not a file, None if not exists - -### documentFileIsDirectory() -Check if path is a directory. - -```python -documentFileIsDirectory(path) -``` - -**Parameters:** -- `path` (str): Path to check - -**Returns:** True if directory, False if not a directory, None if not exists - -### documentFileDelete() -Delete file or directory. - -```python -documentFileDelete(FileOrTree) -``` - -**Returns:** True if success - -### documentFileRenameTo() -Rename or move file. - -```python -documentFileRenameTo(Src, Dest) -``` - -**Returns:** True if success - -### documentFileCopy() -Copy file. - -```python -documentFileCopy(SrcFileOrTree, DestFileOrTree) -``` - -## Stream Operations - -### documentFileInputStream() -Read file content. - -```python -documentFileInputStream(srcFile, EncodingFormat="", skip=None, length=None) -``` - -**Parameters:** -- `srcFile` (str): Source file -- `EncodingFormat` (str): "UTF-8", "GBK", "Base64", or "" for bytes -- `skip` (int): Skip bytes from start -- `length` (int): Read length - -**Returns:** File content - -### documentFileOutputStream() -Write file content. - -```python -documentFileOutputStream(destFile, src, EncodingFormat="", append=None) -``` - -**Parameters:** -- `destFile` (str): Destination file -- `src`: Data to write -- `EncodingFormat` (str): Encoding format -- `append` (bool): Append mode - -## File Information - -### documentFileLength() -Get file size in bytes. - -```python -documentFileLength(path) -``` - -**Parameters:** -- `path` (str): File path - -**Returns:** File size in bytes (0 if not exists) - -### documentFileLastModified() -Get last modified time. - -```python -documentFileLastModified(path) -``` - -**Parameters:** -- `path` (str): File path - -**Returns:** Timestamp (0 if not exists) - -### documentFileGetStat() -Get comprehensive file statistics. - -```python -documentFileGetStat(path) -``` - -**Parameters:** -- `path` (str): File path - -**Returns:** Dict with length, last modified, and read/write permissions, or None if not exists - -## URI Operations - -### documentFileGetUri() -Get URI from path. - -```python -documentFileGetUri(path, isDirectory=None) -``` - -### documentFileShowOpen() -Show file picker. - -```python -documentFileShowOpen() -``` - -**Returns:** Selected file URI - -## Usage Example - -```python -import androidhelper - -droid = androidhelper.Android() - -# Create directory -droid.documentFileMkdir("/sdcard/MyFolder") - -# List files -files = droid.documentFileListFiles("/sdcard").result -for f in files: - print(f) - -# Read file -content = droid.documentFileInputStream( - "/sdcard/test.txt", - EncodingFormat="UTF-8" -).result -print(content) - -# Write file -droid.documentFileOutputStream( - "/sdcard/output.txt", - "Hello World", - EncodingFormat="UTF-8" -) -``` diff --git a/source/en/qsl4a/storage/preferences.md b/source/en/qsl4a/storage/preferences.md deleted file mode 100644 index 1b33246..0000000 --- a/source/en/qsl4a/storage/preferences.md +++ /dev/null @@ -1,81 +0,0 @@ -# Preferences API - -Store and retrieve data using Android SharedPreferences. - -## Preference Methods - -### prefGetValue() -Read a value from shared preferences. - -```python -prefGetValue(key, filename=None) -``` - -**Parameters:** -- `key` (str): Preference key -- `filename` (str, optional): Preference file name - -**Returns:** The stored value (any type) - -### prefPutValue() -Write a value to shared preferences. - -```python -prefPutValue(key, value, filename=None) -``` - -**Parameters:** -- `key` (str): Preference key -- `value` (object): Value to store -- `filename` (str, optional): Preference file name - -### prefGetAll() -Get all preference values. - -```python -prefGetAll(filename=None) -``` - -**Parameters:** -- `filename` (str, optional): Preference file name - -**Returns:** Map of all preferences - -### prefRemoveValue() -Remove a value from shared preferences. - -```python -prefRemoveValue(key, filename=None) -``` - -**Parameters:** -- `key` (str): Preference key to remove -- `filename` (str, optional): Preference file name - -## Usage Example - -```python -import androidhelper - -droid = androidhelper.Android() - -# Store values -droid.prefPutValue("username", "alice") -droid.prefPutValue("score", 100) -droid.prefPutValue("enabled", True) - -# Read a specific value -username = droid.prefGetValue("username").result -print(f"Username: {username}") - -# Get all preferences -all_prefs = droid.prefGetAll().result -print(f"All prefs: {all_prefs}") - -# Remove a value -droid.prefRemoveValue("score") - -# Use custom filename -droid.prefPutValue("token", "abc123", filename="auth.prefs") -token = droid.prefGetValue("token", filename="auth.prefs").result -``` diff --git a/source/en/qsl4a/system/activityresult.md b/source/en/qsl4a/system/activityresult.md deleted file mode 100644 index e097a15..0000000 --- a/source/en/qsl4a/system/activityresult.md +++ /dev/null @@ -1,230 +0,0 @@ -# Activity Result API - -Set activity results for scripts launched via `startActivityForResult`. - -## Result Methods - -### setResultBoolean() -Set a boolean result. - -```python -setResultBoolean(resultCode, resultValue) -``` - -**Parameters:** -- `resultCode` (int): Result code -- `resultValue` (bool): Boolean result value - -### setResultByte() -Set a byte result. - -```python -setResultByte(resultCode, resultValue) -``` - -**Parameters:** -- `resultCode` (int): Result code -- `resultValue` (int): Byte result value - -### setResultShort() -Set a short result. - -```python -setResultShort(resultCode, resultValue) -``` - -**Parameters:** -- `resultCode` (int): Result code -- `resultValue` (int): Short result value - -### setResultChar() -Set a character result. - -```python -setResultChar(resultCode, resultValue) -``` - -**Parameters:** -- `resultCode` (int): Result code -- `resultValue` (str): Character result value - -### setResultInteger() -Set an integer result. - -```python -setResultInteger(resultCode, resultValue) -``` - -**Parameters:** -- `resultCode` (int): Result code -- `resultValue` (int): Integer result value - -### setResultLong() -Set a long result. - -```python -setResultLong(resultCode, resultValue) -``` - -**Parameters:** -- `resultCode` (int): Result code -- `resultValue` (int): Long result value - -### setResultFloat() -Set a float result. - -```python -setResultFloat(resultCode, resultValue) -``` - -**Parameters:** -- `resultCode` (int): Result code -- `resultValue` (float): Float result value - -### setResultDouble() -Set a double result. - -```python -setResultDouble(resultCode, resultValue) -``` - -**Parameters:** -- `resultCode` (int): Result code -- `resultValue` (float): Double result value - -### setResultString() -Set a string result. - -```python -setResultString(resultCode, resultValue) -``` - -**Parameters:** -- `resultCode` (int): Result code -- `resultValue` (str): String result value - -### setResultBooleanArray() -Set a boolean array result. - -```python -setResultBooleanArray(resultCode, resultValue) -``` - -**Parameters:** -- `resultCode` (int): Result code -- `resultValue` (list): Boolean array - -### setResultByteArray() -Set a byte array result. - -```python -setResultByteArray(resultCode, resultValue) -``` - -**Parameters:** -- `resultCode` (int): Result code -- `resultValue` (list): Byte array - -### setResultShortArray() -Set a short array result. - -```python -setResultShortArray(resultCode, resultValue) -``` - -**Parameters:** -- `resultCode` (int): Result code -- `resultValue` (list): Short array - -### setResultCharArray() -Set a character array result. - -```python -setResultCharArray(resultCode, resultValue) -``` - -**Parameters:** -- `resultCode` (int): Result code -- `resultValue` (list): Char array - -### setResultIntegerArray() -Set an integer array result. - -```python -setResultIntegerArray(resultCode, resultValue) -``` - -**Parameters:** -- `resultCode` (int): Result code -- `resultValue` (list): Integer array - -### setResultLongArray() -Set a long array result. - -```python -setResultLongArray(resultCode, resultValue) -``` - -**Parameters:** -- `resultCode` (int): Result code -- `resultValue` (list): Long array - -### setResultFloatArray() -Set a float array result. - -```python -setResultFloatArray(resultCode, resultValue) -``` - -**Parameters:** -- `resultCode` (int): Result code -- `resultValue` (list): Float array - -### setResultDoubleArray() -Set a double array result. - -```python -setResultDoubleArray(resultCode, resultValue) -``` - -**Parameters:** -- `resultCode` (int): Result code -- `resultValue` (list): Double array - -### setResultStringArray() -Set a string array result. - -```python -setResultStringArray(resultCode, resultValue) -``` - -**Parameters:** -- `resultCode` (int): Result code -- `resultValue` (list): String array - -### setResultSerializable() -Set a serializable result. - -```python -setResultSerializable(resultCode, resultValue) -``` - -**Parameters:** -- `resultCode` (int): Result code -- `resultValue`: Serializable result value - -## Usage Example - -```python -import androidhelper - -droid = androidhelper.Android() - -# After performing an activity, set the result -# Example: Return success with data -droid.setResultInteger(0, 200) # RESULT_OK -droid.setResultString(0, "Operation completed successfully") - -# Return an array result -droid.setResultIntegerArray(0, [1, 2, 3, 4, 5]) -``` diff --git a/source/en/qsl4a/system/application.md b/source/en/qsl4a/system/application.md deleted file mode 100644 index 3332654..0000000 --- a/source/en/qsl4a/system/application.md +++ /dev/null @@ -1,269 +0,0 @@ -# Application Management - -Manage applications, launch apps, and query system information. - -## Application Information - -### getApplicationInfo() -Get information about an app. - -```python -getApplicationInfo(packageName=None) -``` - -**Parameters:** -- `packageName` (str): Package name (None = current app) - -**Returns:** App info dict - -### getInstalledPackages() -Get list of installed packages. - -```python -getInstalledPackages(flag=4) -``` - -**Parameters:** -- `flag` (int): Package flag filter (default: 4) - -**Returns:** List of installed packages - -### getRunningPackages() -List running packages. - -```python -getRunningPackages() -``` - -**Returns:** List of package names - -### getLaunchablePackages() -Get list of launchable packages. - -```python -getLaunchablePackages(needClassName=False) -``` - -**Parameters:** -- `needClassName` (bool): Include main activity class name (default: False) - -**Returns:** List of launchable package names or dict with class names - -## Application Control - -### launch() -Launch an application. - -```python -launch(classname=None, packagename=None, wait=True) -``` - -**Parameters:** -- `classname` (str): Main activity class name -- `packagename` (str): Package name -- `wait` (bool): Wait for launch to complete (default: True) - -**Returns:** Launch result - -### forceStopPackage() -Force stop an application. - -```python -forceStopPackage(packageName) -``` - -**Parameters:** -- `packageName` (str): Package name to stop - -## Version Information - -### getPackageVersion() -Get app version name. - -```python -getPackageVersion(packageName) -``` - -**Returns:** Version string (e.g., "3.2.1") - -### getPackageVersionCode() -Get app version code. - -```python -getPackageVersionCode(packageName) -``` - -**Returns:** Version code integer - -### getConstants() -Get class constants. - -```python -getConstants(classname) -``` - -**Parameters:** -- `classname` (str): Full class name - -**Returns:** Dict of constant names and values - -## System Features - -### backgroundProtect() -Enable or disable background protection for the app. - -```python -backgroundProtect(enabled=True) -``` - -**Parameters:** -- `enabled` (bool): True to enable protection, False to disable - -### createScriptShortCut() -Create a home screen shortcut for a script. - -```python -createScriptShortCut(scriptPath, label=None, iconPath=None, scriptArg=None) -``` - -**Parameters:** -- `scriptPath` (str): Path to the Python script -- `label` (str, optional): Shortcut label -- `iconPath` (str, optional): Path to icon image -- `scriptArg` (str, optional): Argument to pass to script - -## Device Information - -### getAndroidID() -Get Android device ID. - -```python -getAndroidID() -``` - -**Returns:** Unique Android device ID string - -### getSysInfo() -Get system information. - -```python -getSysInfo() -``` - -**Returns:** Dict with system details - -### getLocale() -Get device locale. - -```python -getLocale() -``` - -**Returns:** Locale string (e.g., "en_US") - -### getHarmonyOsInformation() -Get HarmonyOS information if running on HarmonyOS. - -```python -getHarmonyOsInformation() -``` - -**Returns:** HarmonyOS version info or None - -### isExternalStorageManager() -Check if app has external storage manager permission. - -```python -isExternalStorageManager() -``` - -**Returns:** True if has permission - -## Memory and Display - -### getMemoryInfo() -Get memory information. - -```python -getMemoryInfo() -``` - -**Returns:** Dict with memory stats - -### getScreenInfo() -Get screen information. - -```python -getScreenInfo() -``` - -**Returns:** Dict with width, height, density - -## Permissions - -### checkPermissions() -Check current app permissions. - -```python -checkPermissions() -``` - -**Returns:** Dict of permissions and their status - -### requestPermissions() -Request permissions from the user. - -```python -requestPermissions(permissions=None) -``` - -**Parameters:** -- `permissions` (list, optional): List of permissions to request. If None, requests all needed permissions. - -**Returns:** Result of permission request - -## System Actions - -### showScreenLock() -Show screen lock (PIN/pattern/password entry). - -```python -showScreenLock() -``` - -## Usage Example - -```python -import androidhelper - -droid = androidhelper.Android() - -# Get system info -info = droid.getSysInfo().result -print(f"System: {info}") - -# Get app version -version = droid.getPackageVersion("org.qpython.qpy").result -print(f"QPython version: {version}") - -# List installed apps -apps = droid.getInstalledPackages().result -print(f"Installed apps: {len(apps)}") - -# Launch an app -droid.launch(packagename="com.android.settings") - -# Check permissions -perms = droid.checkPermissions().result -print(f"Permissions: {perms}") - -# Request storage permission -droid.requestPermissions(["android.permission.READ_EXTERNAL_STORAGE"]) - -# Create shortcut -droid.createScriptShortCut( - "/sdcard/my_script.py", - label="My Script", - iconPath="/sdcard/icon.png" -) -``` diff --git a/source/en/qsl4a/system/battery.md b/source/en/qsl4a/system/battery.md deleted file mode 100644 index 523fa31..0000000 --- a/source/en/qsl4a/system/battery.md +++ /dev/null @@ -1,91 +0,0 @@ -# Battery API - -Monitor device battery status and health. - -## Methods - -### readBatteryData() -Get complete battery information. - -```python -readBatteryData() -``` - -**Returns:** Dict with battery data - -### batteryStartMonitoring() -Start battery monitoring. - -```python -batteryStartMonitoring() -``` - -### batteryStopMonitoring() -Stop battery monitoring. - -```python -batteryStopMonitoring() -``` - -### batteryGetLevel() -Get battery percentage. - -```python -batteryGetLevel() -``` - -**Returns:** Int (0-100) - -### batteryGetStatus() -Get charging status. - -```python -batteryGetStatus() -``` - -**Returns:** -- 1: Unknown -- 2: Charging -- 3: Discharging -- 4: Not charging -- 5: Full - -### batteryGetPlugType() -Get power source. - -```python -batteryGetPlugType() -``` - -**Returns:** -- -1: Unknown -- 0: Unplugged -- 1: AC charger -- 2: USB port - -### batteryGetHealth() -Get battery health. - -```python -batteryGetHealth() -``` - -**Returns:** -- 1: Unknown -- 2: Good -- 3: Overheat -- 4: Dead -- 5: Over voltage -- 6: Unspecified failure - -## Usage Example - -```python -import androidhelper - -droid = androidhelper.Android() - -# Get battery level -level = droid.batteryGetLevel().result -print(f"Battery: {level}%") -``` \ No newline at end of file diff --git a/source/en/qsl4a/system/qpyinterface.md b/source/en/qsl4a/system/qpyinterface.md deleted file mode 100644 index a132a38..0000000 --- a/source/en/qsl4a/system/qpyinterface.md +++ /dev/null @@ -1,131 +0,0 @@ -# QPython Interface API - -Execute QPython scripts and manage shared variables from other apps. - -## Script Execution Methods - -### executeQPy() -Execute a QPython script. - -```python -executeQPy(path="", arg=None) -``` - -**Parameters:** -- `path` (str): Path to the script file -- `arg` (str, optional): Command line arguments - -**Returns:** True if started successfully - -### executeQPyAsSrv() -Execute a QPython script as a service. - -```python -executeQPyAsSrv(path=None) -``` - -**Parameters:** -- `path` (str, optional): Path to the script file - -**Returns:** True if started successfully - -### executeQPyCode() -Execute Python code directly. - -```python -executeQPyCode(code=None) -``` - -**Parameters:** -- `code` (str, optional): Python code to execute - -**Returns:** True if started successfully - -### executeQPyCodeAsSrv() -Execute Python code as a service. - -```python -executeQPyCodeAsSrv(code=None) -``` - -**Parameters:** -- `code` (str, optional): Python code to execute - -**Returns:** True if started successfully - -## Shared Variables - -Shared variables allow communication between QPython and other apps. - -### sharedVariableSet() -Set a Java shared variable. - -```python -sharedVariableSet(key, value) -``` - -**Parameters:** -- `key` (str): Variable name -- `value` (str): Variable value - -**Returns:** The stored value - -### sharedVariableGet() -Get a Java shared variable. - -```python -sharedVariableGet(key) -``` - -**Parameters:** -- `key` (str): Variable name - -**Returns:** The stored value - -### sharedVariableRemove() -Remove a Java shared variable. - -```python -sharedVariableRemove(key) -``` - -**Parameters:** -- `key` (str): Variable name to remove - -**Returns:** The removed value - -### getLastLog() -Get the last log output from QPython. - -```python -getLastLog() -``` - -**Returns:** String log content - -## Usage Example - -```python -import androidhelper - -droid = androidhelper.Android() - -# Execute a script -droid.executeQPy("/sdcard/my_script.py", arg="test") - -# Execute code directly -code = "print('Hello from QPython!')" -droid.executeQPyCode(code) - -# Use shared variables -droid.sharedVariableSet("username", "alice") -username = droid.sharedVariableGet("username").result -print(f"Username: {username}") - -# Remove variable -droid.sharedVariableRemove("username") - -# Get recent log -log = droid.getLastLog().result -print(f"Log: {log}") -``` diff --git a/source/en/qsl4a/system/sensors.md b/source/en/qsl4a/system/sensors.md deleted file mode 100644 index 89e243b..0000000 --- a/source/en/qsl4a/system/sensors.md +++ /dev/null @@ -1,127 +0,0 @@ -# Sensor API - -Access device sensors including accelerometer, gyroscope, magnetometer, and more. - -## Methods - -### startSensingTimed() -Start sensor monitoring with time intervals. - -```python -startSensingTimed(sensorNumber, delayTime) -``` - -**Parameters:** -- `sensorNumber` (int): Sensor ID (1-3 typically) -- `delayTime` (int): Delay between readings in milliseconds - -### startSensingThreshold() -Start sensor monitoring with threshold trigger. - -```python -startSensingThreshold(sensorNumber, threshold, axis) -``` - -**Parameters:** -- `sensorNumber` (int): Sensor ID -- `threshold` (float): Trigger threshold -- `axis` (int): Axis to monitor (0=X, 1=Y, 2=Z) - -### stopSensing() -Stop all sensor monitoring. - -```python -stopSensing() -``` - -### readSensors() -Read current sensor data. - -```python -readSensors() -``` - -**Returns:** Sensor data dict - -### sensorsReadAccelerometer() -Read accelerometer values. - -```python -sensorsReadAccelerometer() -``` - -**Returns:** List [X, Y, Z] in m/s² - -### sensorsReadGyroscope() -Read gyroscope values. - -```python -sensorsReadGyroscope() -``` - -**Returns:** List [X, Y, Z] in rad/s - -### sensorsReadMagnetometer() -Read magnetic field values. - -```python -sensorsReadMagnetometer() -``` - -**Returns:** List [X, Y, Z] in μT - -### sensorsReadOrientation() -Read device orientation. - -```python -sensorsReadOrientation() -``` - -**Returns:** List [azimuth, pitch, roll] in degrees - -### sensorsGetLight() -Read light sensor value. - -```python -sensorsGetLight() -``` - -**Returns:** Light level in lux - -### sensorsGetStepCounter() -Read step counter. - -```python -sensorsGetStepCounter() -``` - -**Returns:** Number of steps - -### sensorsGetAccuracy() -Get the current sensor accuracy. - -```python -sensorsGetAccuracy() -``` - -**Returns:** Accuracy value (0-3: UNRELIABLE, ACCURACY_LOW, ACCURACY_MEDIUM, ACCURACY_HIGH) - -## Usage Example - -```python -import androidhelper -import time - -droid = androidhelper.Android() - -# Start sensing -droid.startSensingTimed(1, 250) - -# Read sensors 10 times -for i in range(10): - accel = droid.sensorsReadAccelerometer().result - print(f"Accel: X={accel[0]:.2f}, Y={accel[1]:.2f}, Z={accel[2]:.2f}") - time.sleep(0.5) - -droid.stopSensing() -``` diff --git a/source/en/qsl4a/system/settings.md b/source/en/qsl4a/system/settings.md deleted file mode 100644 index 905155b..0000000 --- a/source/en/qsl4a/system/settings.md +++ /dev/null @@ -1,234 +0,0 @@ -# Settings API - -Control system settings including screen, sound, and network settings. - -## Screen Settings - -### setScreenTimeout() -Set the screen timeout value. - -```python -setScreenTimeout(value) -``` - -**Parameters:** -- `value` (int): Screen timeout in seconds - -**Returns:** Previous timeout value - -### getScreenTimeout() -Get the current screen timeout. - -```python -getScreenTimeout() -``` - -**Returns:** Current screen timeout in seconds - -### getScreenBrightness() -Get the screen brightness value. - -```python -getScreenBrightness() -``` - -**Returns:** Brightness value (0-255) - -### setScreenBrightness() -Set the screen brightness. - -```python -setScreenBrightness(value=None) -``` - -**Parameters:** -- `value` (int, optional): Brightness value (0-255), or None for auto - -**Returns:** Previous brightness value - -### checkScreenOn() -Check if the screen is on. - -```python -checkScreenOn() -``` - -**Returns:** True if screen is on, False otherwise - -## Airplane Mode - -### checkAirplaneMode() -Check if airplane mode is enabled. - -```python -checkAirplaneMode() -``` - -**Returns:** True if airplane mode is on - -## Ringer Settings - -### checkRingerSilentMode() -Check if ringer is in silent mode. - -```python -checkRingerSilentMode() -``` - -**Returns:** True if silent mode is on - -### toggleRingerSilentMode() -Toggle ringer silent mode. - -```python -toggleRingerSilentMode(enabled=None) -``` - -**Parameters:** -- `enabled` (bool, optional): True to enable, False to disable, None to toggle - -**Returns:** New state - -### toggleVibrateMode() -Toggle vibrate mode. - -```python -toggleVibrateMode(enabled=None, ringer=None) -``` - -**Parameters:** -- `enabled` (bool, optional): Toggle vibrate on/off -- `ringer` (bool, optional): Apply to ringer mode - -**Returns:** New state - -### getVibrateMode() -Get the vibrate mode setting. - -```python -getVibrateMode(ringer=None) -``` - -**Parameters:** -- `ringer` (bool, optional): Check ringer vibrate mode - -**Returns:** True if vibrate is enabled - -## Volume Settings - -### getRingerVolume() -Get the current ringer volume. - -```python -getRingerVolume() -``` - -**Returns:** Ringer volume level (0-7 typically) - -### getMaxRingerVolume() -Get the maximum ringer volume. - -```python -getMaxRingerVolume() -``` - -**Returns:** Maximum ringer volume - -### setRingerVolume() -Set the ringer volume. - -```python -setRingerVolume(volume) -``` - -**Parameters:** -- `volume` (int): Volume level - -### getMediaVolume() -Get the current media volume. - -```python -getMediaVolume() -``` - -**Returns:** Media volume level (0-15 typically) - -### getMaxMediaVolume() -Get the maximum media volume. - -```python -getMaxMediaVolume() -``` - -**Returns:** Maximum media volume - -### setMediaVolume() -Set the media volume. - -```python -setMediaVolume(volume) -``` - -**Parameters:** -- `volume` (int): Volume level - -## System Info - -### elapsedRealtimeNanos() -Get nanoseconds since system startup. - -```python -elapsedRealtimeNanos() -``` - -**Returns:** Nanoseconds (can be used for timing) - -### getTrafficStats() -Get network traffic statistics. - -```python -getTrafficStats(flags=7) -``` - -**Parameters:** -- `flags` (int): Which stats to retrieve (default: 7 = all) - -**Returns:** Dict with transmit/receive bytes - -### getAppTxBytes() -Get transmit bytes for QPython app. - -```python -getAppTxBytes(packageName) -``` - -**Parameters:** -- `packageName` (str): Package name - -**Returns:** Dict with tx/rx bytes - -## Usage Example - -```python -import androidhelper - -droid = androidhelper.Android() - -# Screen settings -current_timeout = droid.getScreenTimeout().result -print(f"Current timeout: {current_timeout}s") -droid.setScreenTimeout(30) - -# Check screen -if droid.checkScreenOn().result: - print("Screen is on") - -# Volume control -media_vol = droid.getMediaVolume().result -print(f"Media volume: {media_vol}") -droid.setMediaVolume(10) - -# Check airplane mode -if droid.checkAirplaneMode().result: - print("Airplane mode is on") -``` diff --git a/source/en/qsl4a/system/sysinfo.md b/source/en/qsl4a/system/sysinfo.md deleted file mode 100644 index cfd66c5..0000000 --- a/source/en/qsl4a/system/sysinfo.md +++ /dev/null @@ -1,90 +0,0 @@ -# System Information - -Retrieve device and system information. - -## Device Information - -### getAndroidID() -Get the Android device ID. - -```python -getAndroidID() -``` - -**Returns:** String device ID - -### getSysInfo() -Get comprehensive system information. - -```python -getSysInfo() -``` - -**Returns:** Dict with system details - -### getLocale() -Get device locale settings. - -```python -getLocale() -``` - -**Returns:** Locale string (e.g., "en_US") - -## Memory - -### getMemoryInfo() -Get RAM information. - -```python -getMemoryInfo() -``` - -**Returns:** Dict with memory stats - -## Display - -### getScreenInfo() -Get display information. - -```python -getScreenInfo() -``` - -**Returns:** Dict with width, height, density - -## Identifiers - -### getImei() -Get device IMEI. - -```python -getImei(slotIndex=None) -``` - -### getMeid() -Get device MEID. - -```python -getMeid(slotIndex=None) -``` - -## Usage Example - -```python -import androidhelper - -droid = androidhelper.Android() - -# Device ID -android_id = droid.getAndroidID().result -print(f"Android ID: {android_id}") - -# Screen info -screen = droid.getScreenInfo().result -print(f"Screen: {screen['width']}x{screen['height']}") - -# Memory -memory = droid.getMemoryInfo().result -print(f"Memory: {memory}") -``` diff --git a/source/en/qsl4a/system/wakelock.md b/source/en/qsl4a/system/wakelock.md deleted file mode 100644 index 4fc5ccf..0000000 --- a/source/en/qsl4a/system/wakelock.md +++ /dev/null @@ -1,78 +0,0 @@ -# WakeLock API - -Control device wake locks to keep the CPU or screen on. - -## Wake Lock Types - -QSL4A provides different wake lock types: - -| Type | Description | -|------|-------------| -| Full | CPU on, screen bright, keyboard bright | -| Partial | CPU on only | -| Bright | CPU on, screen bright | -| Dim | CPU on, screen dim | - -## Wake Lock Methods - -### wakeLockAcquireFull() -Acquire a full wake lock (CPU on, screen bright, keyboard bright). - -```python -wakeLockAcquireFull() -``` - -### wakeLockAcquirePartial() -Acquire a partial wake lock (CPU on only). - -```python -wakeLockAcquirePartial() -``` - -### wakeLockAcquireBright() -Acquire a bright wake lock (CPU on, screen bright). - -```python -wakeLockAcquireBright() -``` - -### wakeLockAcquireDim() -Acquire a dim wake lock (CPU on, screen dim). - -```python -wakeLockAcquireDim() -``` - -### wakeLockRelease() -Release the wake lock. - -```python -wakeLockRelease() -``` - -## Usage Example - -```python -import androidhelper -import time - -droid = androidhelper.Android() - -# Acquire full wake lock -droid.wakeLockAcquireFull() - -# Do important work while keeping screen on -print("Screen will stay on") -time.sleep(10) - -# Release when done -droid.wakeLockRelease() - -# Or use partial lock for background tasks -droid.wakeLockAcquirePartial() -# CPU stays on even with screen off -time.sleep(30) -droid.wakeLockRelease() -``` - -**Note:** Remember to release wake locks when no longer needed to conserve battery. diff --git a/source/en/qsl4a/ui/accessibility.md b/source/en/qsl4a/ui/accessibility.md deleted file mode 100644 index e1f967d..0000000 --- a/source/en/qsl4a/ui/accessibility.md +++ /dev/null @@ -1,123 +0,0 @@ -# Accessibility Service - -The Accessibility Service allows automation of UI interactions like clicks, swipes, and system actions. - -## Service Control - -### accessibilityStartService() -Start the accessibility service. - -```python -accessibilityStartService() -``` - -**Returns:** `True` if successful, `False` otherwise - -### accessibilityServiceEnabled() -Check if accessibility service is enabled. - -```python -accessibilityServiceEnabled() -``` - -**Returns:** `True` or `False` - -## Screen Interactions - -### accessibilityClick() -Click at screen coordinates. - -```python -accessibilityClick(x=0, y=0, t=50) -``` - -**Parameters:** -- `x` (int/float): X coordinate (0=center, decimals supported) -- `y` (int/float): Y coordinate (0=center, decimals supported) -- `t` (int): Press duration in milliseconds (default: 50) - -### accessibilitySlide() -Multi-point slide gesture. - -```python -accessibilitySlide(XnYn=None, t=None) -``` - -**Parameters:** -- `XnYn` (list): Coordinates [X1, Y1, X2, Y2, ... Xn, Yn] -- `t` (int): Slide duration (default: 50*n ms) - -## System Actions - -### accessibilityAction() -Execute system action by code. - -```python -accessibilityAction(actionCode) -``` - -**Action Codes:** -| Code | Constant | Description | -|------|----------|-------------| -| 1 | BACK | Back button | -| 2 | HOME | Home button | -| 3 | RECENTS | Recent apps | -| 4 | NOTIFICATIONS | Open notifications | -| 5 | QUICK_SETTINGS | Open quick settings | -| 6 | POWER_DIALOG | Power menu | -| 7 | TOGGLE_SPLIT_SCREEN | Split screen | -| 8 | LOCK_SCREEN | Lock screen | -| 9 | TAKE_SCREENSHOT | Take screenshot | -| 10 | KEYCODE_HEADSETHOOK | Headset hook | -| 11-14 | ACCESSIBILITY_* | Accessibility buttons | -| 15 | DISMISS_NOTIFICATION_SHADE | Close notifications | -| 16-20 | DPAD_* | Directional pad | -| 21 | MENU | Menu button | -| 22 | MEDIA_PLAY_PAUSE | Play/Pause media | - -## Usage Examples - -### Click Center Screen - -```python -import androidhelper - -droid = androidhelper.Android() - -# Start service -if not droid.accessibilityServiceEnabled().result: - droid.accessibilityStartService() - -# Click center (0,0 = center) -droid.accessibilityClick(0, 0, t=100) -``` - -### Swipe Gesture - -```python -# Swipe from bottom to top (scroll up) -droid.accessibilitySlide([0, 1.5, 0, -1.5], t=300) -``` - -### System Navigation - -```python -# Press home -droid.accessibilityAction(2) - -# Press back -droid.accessibilityAction(1) - -# Open notifications -droid.accessibilityAction(4) -``` - -### Tap Specific Location - -```python -# Tap at screen coordinate (500, 800) -droid.accessibilityClick(500, 800) - -# Tap at relative position (center horizontally, 3/4 down) -droid.accessibilityClick(0, 1.5) -``` diff --git a/source/en/qsl4a/ui/dialogs.md b/source/en/qsl4a/ui/dialogs.md deleted file mode 100644 index a5e5749..0000000 --- a/source/en/qsl4a/ui/dialogs.md +++ /dev/null @@ -1,321 +0,0 @@ -# Dialog System - -QSL4A provides comprehensive dialog support for user interaction, including alerts, input dialogs, choice dialogs, and progress dialogs. - -## Alert Dialogs - -### dialogShowAlert() -Show a simple alert dialog. - -```python -dialogShowAlert(title="Alert", message="The message of the alert.", - positiveButtonText="OK", negativeButtonText=None, - neutralButtonText=None, messageIsHtml=False) -``` - -**Parameters:** -- `title` (str): Dialog title -- `message` (str): Message text -- `positiveButtonText` (str): Positive button label -- `negativeButtonText` (str, optional): Negative button label -- `neutralButtonText` (str, optional): Neutral button label -- `messageIsHtml` (bool): Parse message as HTML - -**Returns:** Result with button clicked - -### dialogShowSimpleChoice() -Show a simple choice dialog with items. - -```python -dialogShowSimpleChoice(title="Alert", message="The message of the alert.", - items=None, positiveButtonText="OK", - negativeButtonText=None, neutralButtonText=None, - messageIsHtml=False) -``` - -**Parameters:** -- `items` (list): List of choice strings - -**Returns:** Result with selected item - -## Input Dialogs - -### dialogGetInput() -Get text input from user. - -```python -dialogGetInput(title="Value", message="Please enter value:", - defaultText=None, messageIsHtml=False) -``` - -**Returns:** Result with user's input text - -### dialogGetPassword() -Get password input. - -```python -dialogGetPassword(title="Password", message="Please enter password:") -``` - -**Returns:** Result with password entered - -### dialogCreateInput() -Create a custom input dialog. - -```python -dialogCreateInput(title="Value", message="Please enter value:", - defaultText=None, inputType=None) -``` - -**Parameters:** -- `inputType` (str): Input type (e.g., "text", "number", "textPassword") - -### dialogCreatePassword() -Create a password input dialog. - -```python -dialogCreatePassword(title="Password", message="Please enter password:") -``` - -### dialogCreateSeekBar() -Create a seek bar/slider dialog. - -```python -dialogCreateSeekBar(starting_value=50, maximum_value=100, title="", message="") -``` - -**Parameters:** -- `starting_value` (int): Initial value -- `maximum_value` (int): Maximum value - -## Choice Dialogs - -### dialogShowSingleChoice() -Show single choice (radio) dialog. - -```python -dialogShowSingleChoice(title="Alert", message="The message of the alert.", - items=None, selected=-1, positiveButtonText="OK", - negativeButtonText=None, neutralButtonText=None, - messageIsHtml=False) -``` - -**Parameters:** -- `items` (list): List of choice strings -- `selected` (int): Default selected index - -### dialogShowMultiChoice() -Show multiple choice (checkbox) dialog. - -```python -dialogShowMultiChoice(title="Alert", message="The message of the alert.", - items=None, selected=None, positiveButtonText="OK", - negativeButtonText=None, neutralButtonText=None, - messageIsHtml=False) -``` - -**Parameters:** -- `items` (list): List of choice strings -- `selected` (list): List of initially selected indices - -### dialogSetSingleChoiceItems() -Set single choice items for a dialog. - -```python -dialogSetSingleChoiceItems(items, selected=-1) -``` - -### dialogSetMultiChoiceItems() -Set multiple choice items for a dialog. - -```python -dialogSetMultiChoiceItems(items, selected=None) -``` - -## Progress Dialogs - -### dialogCreateSpinnerProgress() -Create indeterminate progress dialog. - -```python -dialogCreateSpinnerProgress(title=None, message=None, maximum_progress=100) -``` - -### dialogCreateHorizontalProgress() -Create horizontal progress dialog. - -```python -dialogCreateHorizontalProgress(title=None, message=None, maximum_progress=100) -``` - -### dialogSetCurrentProgress() -Update progress value. - -```python -dialogSetCurrentProgress(current) -``` - -### dialogSetMaxProgress() -Set maximum progress value. - -```python -dialogSetMaxProgress(max) -``` - -### dialogSetProgressMessage() -Update the progress dialog message. - -```python -dialogSetProgressMessage(message) -``` - -## Picker Dialogs - -### dialogCreateDatePicker() -Create date picker dialog. - -```python -dialogCreateDatePicker(year=1970, month=1, day=1) -``` - -### dialogCreateTimePicker() -Create time picker dialog. - -```python -dialogCreateTimePicker(hour=0, minute=0, is24hour=False) -``` - -## Custom Dialog Creation - -### dialogCreateAlert() -Create a custom alert dialog. - -```python -dialogCreateAlert(title=None, message=None) -``` - -Creates an empty alert dialog. Use with other dialogSet* functions to customize. - -### dialogSetItems() -Set simple list items for the dialog. - -```python -dialogSetItems(items) -``` - -### dialogSetPositiveButtonText() -Set positive button text. - -```python -dialogSetPositiveButtonText(text) -``` - -### dialogSetNegativeButtonText() -Set negative button text. - -```python -dialogSetNegativeButtonText(text) -``` - -### dialogSetNeutralButtonText() -Set neutral button text. - -```python -dialogSetNeutralButtonText(text) -``` - -### dialogSetMessageIsHtml() -Set whether message should be parsed as HTML. - -```python -dialogSetMessageIsHtml(messageIsHtml=True) -``` - -### dialogShow() -Show the created custom dialog. - -```python -dialogShow() -``` - -## Dialog Control - -### dialogDismiss() -Dismiss current dialog. - -```python -dialogDismiss() -``` - -### dialogGetResponse() -Get dialog response. - -```python -dialogGetResponse() -``` - -### dialogGetSelectedItems() -Get selected items from choice dialog. - -```python -dialogGetSelectedItems() -``` - -## Usage Examples - -### Simple Alert - -```python -import androidhelper - -droid = androidhelper.Android() - -# Show alert -droid.dialogShowAlert("Warning", "This is an important message!") -``` - -### Input Dialog - -```python -# Get user input -result = droid.dialogGetInput("Name", "Enter your name:", "John").result -print(f"Hello, {result}!") -``` - -### Custom Dialog with Buttons - -```python -# Create custom dialog -droid.dialogCreateAlert("Custom", "Choose an option") -droid.dialogSetItems(["Option 1", "Option 2", "Option 3"]) -droid.dialogShow() - -# Get response -response = droid.dialogGetResponse().result -print(f"Selected: {response['item']}") -``` - -### Progress Dialog - -```python -# Create progress dialog -droid.dialogCreateHorizontalProgress("Loading", "Please wait...", 100) -droid.dialogShow() - -# Update progress -for i in range(101): - droid.dialogSetCurrentProgress(i) - time.sleep(0.05) - -droid.dialogDismiss() -``` - -### Date Picker - -```python -# Show date picker -droid.dialogCreateDatePicker(2024, 1, 15) -droid.dialogShow() -response = droid.dialogGetResponse().result -print(f"Selected: {response}") -``` diff --git a/source/en/qsl4a/ui/floatview.md b/source/en/qsl4a/ui/floatview.md deleted file mode 100644 index f1b05be..0000000 --- a/source/en/qsl4a/ui/floatview.md +++ /dev/null @@ -1,174 +0,0 @@ -# FloatView - -Floating window support for overlay UI elements that stay on top of other applications. - -## Methods - -### floatView() -Show or modify a floating view. - -```python -floatView(Args=None) -``` - -**Parameters:** -- `Args` (dict): Configuration dictionary with the following keys: - - `index` (int): Float view index (-1 to create new, >=0 to modify existing) - - `text` (str): Text content to display - - `html` (str): HTML content (used if text is omitted) - - `width` (int): View width in pixels (default: 300) - - `height` (int): View height in pixels (default: 150) - - `x` (int): X position (0 = center, positive/negative for offset) - - `y` (int): Y position (0 = center, positive/negative for offset) - - `backColor` (str): Background color in ARGB hex (default: '7f7f7f7f') - - `textColor` (str): Text color in ARGB hex (default: 'ff000000') - - `textSize` (int): Text size (default: 10) - - `textAlign` (int): Text alignment (0 = inherit) - - `script` (str): Script path to run after long click close - - `arg`: Script argument - - `clickRemove` (bool): Enable click to remove (default: True) - - `flag` (int): Window flag (default: 40 - touchable) - -**Returns:** Current chain list length - -### floatViewCount() -Get the number of active float views. - -```python -floatViewCount() -``` - -**Returns:** Number of float views - -### floatViewResult() -Get the result/status of a float view. - -```python -floatViewResult(index=-1) -``` - -**Parameters:** -- `index` (int): Float view index (default: -1, returns last operation result) - -**Returns:** Dict with operation details including: - - `x`, `y`: Position - - `time`: Timestamp - - `operation`: Operation type ('initial', 'move', etc.) - - `index`: View index - - `removed`: True if view was removed - -### floatViewRemove() -Remove a float view. - -```python -floatViewRemove(index=-1) -``` - -**Parameters:** -- `index` (int): Index of view to remove (default: -1 removes the last one) - -**Returns:** 1 if successful, 0 otherwise - -## Constants - -- `floatView.INDEX_NEW = -1` - Create new float view -- `floatView.FLAG_DEFAULT_TOUCHABLE = 40` - Default touchable flag -- `floatView.TEXT_ALIGNMENT_INHERIT = 0` -- `floatView.TEXT_ALIGNMENT_CENTER` - Center text alignment - -## Usage Examples - -### Basic Float View - -```python -import androidhelper -from androidhelper import Android - -droid = androidhelper.Android() - -# Create a simple float view -droid.floatView({ - 'index': -1, # Create new - 'text': 'Hello World', - 'width': 400, - 'height': 300, - 'x': -300, # Offset from center - 'y': -400, - 'backColor': 'ff0000', # Red background - 'textColor': '0000ff', # Blue text - 'textSize': 16, - 'textAlign': droid.floatView.TEXT_ALIGNMENT_CENTER -}) - -# Check count -print(f"Float views: {droid.floatViewCount().result}") - -# Get result -result = droid.floatViewResult().result -print(f"View info: {result}") - -# Remove the float view -droid.floatViewRemove(0) -``` - -### HTML Content - -```python -# Create float view with HTML content -droid.floatView({ - 'text': '', # Empty text to use HTML - 'html': '

Title

Rich HTML content

', - 'width': 500, - 'height': 400 -}) -``` - -### Modify Existing View - -```python -# Create initial view -droid.floatView({'text': 'Initial Text', 'width': 300, 'height': 150}) - -# Modify the same view (index 0) -droid.floatView({ - 'index': 0, - 'text': 'Updated Text!', - 'backColor': '7f00ff00' # Green background -}) - -# Check the move/change result -result = droid.floatViewResult(0).result -print(f"Operation: {result.get('operation')}, Position: ({result.get('x')}, {result.get('y')})") -``` - -### Multiple Float Views - -```python -# Create multiple views -for i in range(3): - droid.floatView({ - 'index': -1, - 'text': f'View {i}', - 'x': i * 100 - 150, - 'y': i * 100 - 150, - 'backColor': f'{i}f{i}f{i}f' - }) - -print(f"Total views: {droid.floatViewCount().result}") - -# Remove all views -while droid.floatViewCount().result > 0: - droid.floatViewRemove() -``` - -### With Script Callback - -```python -# Create float view that runs script on close -droid.floatView({ - 'text': 'Click to close and run script', - 'script': '/sdcard/my_script.py', - 'arg': 'hello from float view', - 'clickRemove': True -}) -``` diff --git a/source/en/qsl4a/ui/fullscreen.md b/source/en/qsl4a/ui/fullscreen.md deleted file mode 100644 index 04d917d..0000000 --- a/source/en/qsl4a/ui/fullscreen.md +++ /dev/null @@ -1,171 +0,0 @@ -# FullScreen UI - -Create custom fullscreen interfaces with native Android layouts. - -## Layout Methods - -### fullShow() -Show a fullscreen layout. - -```python -fullShow(layout, title=None, theme=None) -``` - -**Parameters:** -- `layout` (str): JSON layout definition or layout string -- `title` (str, optional): Window title -- `theme` (str, optional): Theme name - -**Returns:** Window ID - -### fullDismiss() -Close fullscreen window. - -```python -fullDismiss() -``` - -### fullQuery() -Query all widget values. - -```python -fullQuery() -``` - -**Returns:** Dict of widget IDs and values - -### fullQueryDetail() -Query specific widget details. - -```python -fullQueryDetail(id) -``` - -## Property Methods - -### fullGetProperty() -Get widget property. - -```python -fullGetProperty(id, property) -``` - -### fullSetProperty() -Set widget property. - -```python -fullSetProperty(id, property, value) -``` - -### fullSetList() -Set list widget items. - -```python -fullSetList(id, list, isHtml=False, listType=0) -``` - -### fullSetList2() -Set list widget items with resource ID. - -```python -fullSetList2(id, list, intRes) -``` - -**Parameters:** -- `id` (str): Widget ID -- `list` (list): List of items -- `intRes` (int): Android resource ID for list item layout - -### fullSetListSelected() -Set selected item in a list. - -```python -fullSetListSelected(id, selected) -``` - -**Parameters:** -- `id` (str): List widget ID -- `selected` (int): Index of item to select - -### fullGetListSelected() -Get the currently selected list item index. - -```python -fullGetListSelected(id) -``` - -**Parameters:** -- `id` (str): List widget ID - -**Returns:** Selected item index - -## Batch Property Operations - -### fullGetProperties() -Get properties for multiple widgets at once. - -```python -fullGetProperties(ids, property) -``` - -**Parameters:** -- `ids` (list): List of widget IDs -- `property` (str): Property name to get - -**Returns:** Dict mapping widget IDs to property values - -### fullSetProperties() -Set property for multiple widgets at once. - -```python -fullSetProperties(ids, property, value) -``` - -**Parameters:** -- `ids` (list): List of widget IDs -- `property` (str): Property name to set -- `value`: Property value - -## Screenshot - -### fullGetScreenShot() -Capture fullscreen screenshot. - -```python -fullGetScreenShot(path=None) -``` - -**Parameters:** -- `path` (str, optional): Save path. If None, returns in event - -**Returns:** Event with screenshot data - -## Usage Example - -```python -import androidhelper - -droid = androidhelper.Android() - -# Define layout -layout = ''' -{ "type": "LinearLayout", "orientation": "vertical", - "children": [ - { "type": "TextView", "id": "title", "text": "Hello" }, - { "type": "Button", "id": "btn", "text": "Click Me" } - ] -} -''' - -# Show layout -droid.fullShow(layout, "My App") - -# Query button click -event = droid.eventWaitFor('click', timeout=10) -if event.result: - widget_id = event.result['data']['id'] - if widget_id == 'btn': - print("Button clicked!") - -droid.fullDismiss() -``` \ No newline at end of file diff --git a/source/en/tutorial-hello-world.md b/source/en/tutorial-hello-world.md deleted file mode 100644 index cca8f09..0000000 --- a/source/en/tutorial-hello-world.md +++ /dev/null @@ -1,118 +0,0 @@ -# Writing "Hello World" - -## Hello world - -![hello world](static/qpy_helloworld.jpg) - -Well, after you became a bit more familiar with QPython, let's create our first program in QPython. Obviously, it will be `helloworld.py`. ;) - -Start QPython, open editor and enter the following code: - -```python -#qpy:quiet -import androidhelper - -droid = androidhelper.Android() -droid.makeToast('Hello, QPYTHON!') -``` - -No wonder, it's just similar to any other hello-world program. When executed, it just shows pop-up message on the screen (see screenshot on the top). Anyway, it's a good example of QPython program. - -## Code Understanding - -It begins with *import androidhelper* — the most useful module in QPython, which encapsulates almost all interface with Android available in Python. Any script developed in QPython starts with this statement (at least if it claims to communicate with user). Read more about Python library [here](https://docs.python.org/3.12/library/index.html) and import statement [here](https://docs.python.org/3.12/reference/simple_stmts.html#import). - -Next, we create an object `droid` (actually a class), which is necessary to call RPC functions to communicate with Android. - -The last line of our code calls `droid.makeToast()`, which shows a small pop-up message (a "toast") on the screen. - -Well, let's add some more functionality. Let it ask the user name and greet them. - -## More samples - -We can display a simple dialog box with the title, prompt, edit field and buttons **Ok** and **Cancel** using `dialogGetInput` call. Replace the last line of your code and save it as `hello1.py`: - -```python -#qpy:quiet -import androidhelper - -droid = androidhelper.Android() -respond = droid.dialogGetInput("Hello", "What is your name?") -``` - -Well, I think it should return any respond, any user reaction. That's why I wrote `respond = ...`. But what does the call actually return? Let's check. Just add print statement after the last line: - -```python -#qpy:quiet -import androidhelper - -droid = androidhelper.Android() -respond = droid.dialogGetInput("Hello", "What is your name?") -print(respond) -``` - -Then save and run it... - -Oops! Nothing printed? Don't worry. Just pull notification bar and you will see "QPython Program Output: hello1.py" — tap it! - -As you can see, `droid.dialogGetInput()` returns a JSON object with three fields. We need only one — `result` which contains an actual input from user. - -Let's add script's reaction: - -```python -#qpy:quiet -import androidhelper - -droid = androidhelper.Android() -respond = droid.dialogGetInput("Hello", "What is your name?") -print(respond) -message = f'Hello, {respond.result}!' -droid.makeToast(message) -``` - -Last two lines (1) format the message and (2) show the message to the user in the toast. See [Python docs](https://docs.python.org/3.12/tutorial/inputoutput.html#fancier-output-formatting) if you still don't know what f-strings mean. - -Wow! It works! ;) - -Now I'm going to add a bit of logic there. Think: what happen if the user clicks **Cancel** button, or clicks **Ok** leaving the input field blank? - -You can play with the program checking what contains `respond` variable in every case. - -First of all, I want to put text entered by user to a separate variable: `name = respond.result`. Then I'm going to check it, and if it contains any real text, it will be considered as a name and will be used in greeting. Otherwise another message will be shown. Replace fifth line `message = f'Hello, {respond.result}!'` with the following code: - -```python -name = respond.result -if name: - message = f'Hello, {name}!' -else: - message = "Hey! And you're not very polite, %Username%!" -``` - -Use **<** and **>** buttons on the toolbar to indent/unindent lines in if-statement (or just use space/backspace keys). You can read more about indentation in Python [here](https://docs.python.org/3.12/tutorial/introduction.html#first-steps-towards-programming); if-statement described [here](https://docs.python.org/3.12/tutorial/controlflow.html#if-statements). - -First of all, we put user input to the variable `name`. Then we check does `name` contain anything? In case the user left the line blank and clicked **Ok**, the return value is empty string `''`. In case of **Cancel** button pressed, the return value is `None`. Both are treated as false in if-statement. So, only if `name` contains anything meaningful, then-statement is executed and greeting "Hello, ...!" shown. In case of empty input the user will see "Hey! And you're not very polite, %Username%!" message. - -Ok, here is the whole program: - -```python -#qpy:quiet -import androidhelper - -droid = androidhelper.Android() -respond = droid.dialogGetInput("Hello", "What is your name?") -print(respond) -name = respond.result -if name: - message = f'Hello, {name}!' -else: - message = "Hey! And you're not very polite, %Username%!" -droid.makeToast(message) -``` - -## Execution Result - - - -## Next Steps - -For Python beginners, we recommend learning from the [Python Basic Syntax](https://www.qpython.com.cn/courses/python-basic-syntax/) course to further your Python skills, or browse [QPython Featured Courses](https://www.qpython.com.cn/courses/) to find more content you want to learn. diff --git a/source/en/whats-new.md b/source/en/whats-new.md deleted file mode 100644 index d06258c..0000000 --- a/source/en/whats-new.md +++ /dev/null @@ -1,119 +0,0 @@ -# What's New - -## v3.9.2 / v3.9.3 - -- SDK upgrade with support for 16 KB page size, providing a smoother runtime environment -- Major editor updates for a more fluid editing experience -- Various other minor improvements - -## v3.9.1 - -- SDK upgrade with support for 16 KB page size, providing a smoother runtime environment -- Extension packages now support MCP -- Bug fixes - -## v3.9.0 - -- SDK upgrade to incorporate the latest Android features -- Built-in Ollama 0.9.5 upgrade: Now supports running Gemma3n models locally in QPython, experiencing more powerful on-device AI computing capabilities -- Pygame game development support: Combined with XServer, you can now easily write and run Pygame games on QPython, unleashing your creativity! -- Personalized icon customization: Customize your desktop icons and themes through QPython settings, creating a unique programming environment - -## v3.8.11 - -- Built-in Ollama 0.9.5 upgrade: Now supports running Gemma3n models locally in QPython, experiencing more powerful on-device AI computing capabilities -- Pygame game development support: Combined with XServer, you can now easily write and run Pygame games on QPython, unleashing your creativity! -- Personalized icon customization: Customize your desktop icons and themes through QPython settings, creating a unique programming environment - -## v3.8.10 - -- Built-in Ollama upgrade: Now you can easily run Qwen3 / Gemma3 models locally in QPython! -- Powerful MCP library added: Added mcp library in Extensions -> AIPY, allowing you to take powerful MCP functionality with you anywhere, anytime! - -## v3.8.9 - -Major update! AI programming fully integrated into QPython to make your programming easier! - -- Natural language programming support: New support for AIPyApp, enabling natural language programming in QPython. Currently in beta - join our community for usage instructions -- New QSL4A feature: Added notebookOpen function, supporting natural language control to open Notebook files -- Built-in editor upgrade: Enhanced editor functionality, supporting opening and browsing various text files -- Convenient file management: Added internal storage entry in file manager for quick access to your files - -## v3.8.8 - -- Important improvements to file access permission controls, allowing users to flexibly enable or disable access to external storage files through QPython programming -- SDK upgraded to enhance support and compatibility with newer Android versions -- Added Anthropic and Google GenAI libraries in Extensions -> AIPY - -## v3.8.7 - -- Based on existing Tkinter support, added sample code for drawing Doraemon using Turtle in Extensions -> Tools -- Optimized phone permission acquisition process, improving user experience and operational convenience -- Added Google Gen AI library in Extensions -> AIPY for easy access to Gemini Developer API and Vertex AI - -## v3.8.6 - -- Tkinter support: Now supports Tkinter library, accessible through XServer. Please visit www.qpython.org's drive link "Extra" to download required files -- Storage permission update: To improve user experience, we added permission to read phone storage for accessing Python programs stored in other directories - -After this update, QPython and QPythonPlus (not available on app stores, designed for advanced users with more sensitive permissions) will maintain synchronized version numbers. - -## v3.5.2 (2025/2/25) - -Achieve seamless integration with the open source LLM deployment tool Ollama and DeepSeek developed by DeepSeek! - -**Features:** -- Zero threshold to run various large language models locally on mobile devices -- Quickly deploy cutting-edge AI models such as DeepSeek -- Enjoy a minimalist API calling experience -- Build a completely offline intelligent programming environment - -**Capabilities:** -- Load/manage LLM models directly on the mobile phone -- Real-time low-latency response based on local computing - -## v3.5.0 - -- Python upgraded to 3.12.8 -- Improved Dashboard for clearer, more user-friendly functionality -- Added numerous third-party packages: PyTorch / Twisted / Scrapy / FastAPI ... - -## v3.3.8 - -- Fixed NumPy compatibility issues - -## v3.3.5 - -- Upgraded Python kernel to 3.11.9 -- Fixed bug where module installation status was not displayed in extensions -- Fixed bug where deleting modules in extensions failed -- Fixed other bugs - -## v3.3.4 - -- Added OpenAI/Langchain/APIGPTCloud AI packages -- Removed unnecessary files to reduce size - -## v3.2.5 - -- Added some SL4A functions -- Other bug fixes - -## v3.2.3 - -- Updated Python version to 3.11.0 -- Updated IPython version to 8.6.0 -- Updated pip version to 22.3.1 -- Updated scientific computing packages with automatic soft links -- Reduced space usage -- Added some SL4A functions + other bug fixes - -## v3.1.8 - -- Added operation hotkeys in terminal -- Fixed issue where editor lost content on rotation -- Fixed other bugs - ---- - -[Download on Google Play](https://play.google.com/store/apps/details?id=org.qpython.qpy) diff --git a/source/favicon.ico b/source/favicon.ico deleted file mode 100644 index 5650a96..0000000 Binary files a/source/favicon.ico and /dev/null differ diff --git a/source/zh/AIPyApp.md b/source/zh/AIPyApp.md deleted file mode 100644 index 6d22000..0000000 --- a/source/zh/AIPyApp.md +++ /dev/null @@ -1,87 +0,0 @@ -# AIPyApp - AI驱动的程序生成器 - -AIPyApp 是 QPython 中的智能工具,可以利用 AI 从自然语言指令自动生成 Python 程序。 - -![AIPyApp](static/aipyapp_demo.jpg) - -## 概述 - -AIPyApp 改变了您编写代码的方式——只需用自然语言描述您想要的功能,AI 就会为您生成 Python 程序。QPython 还将推出 **AIPy Academy**——一个为 AI 时代量身定制的 Python 编程课程平台。 - -## 安装 - - - -QPython 氛围编程(Vibe Coding) 01 - 如何启用AIPyApp - -### 第一步:从仪表盘启动 - -1. 打开 QPython,进入 **仪表盘** -2. **长按**开始按钮 - -如果未安装 AIPyApp,系统会提示您确认安装。按 **回车键**继续。 - -QPython 将自动从 PYPI 下载并安装所需的依赖项。请耐心等待安装完成。 - -### 第二步:重新启动 AIPyApp - -安装完成后,返回 QPython 仪表盘,再次 **长按**开始按钮启动 AIPyApp。 - -## 配置 - -### 设置您的 AI 密钥 - -首次启动时,您需要提供 AI API 密钥: - -1. **在 PGPT 注册**:在 [https://user.pgpt.cloud](https://user.pgpt.cloud) 创建账户以生成您的 AI 密钥 -2. **高级选项**:AIPyApp 还支持来自 OpenAI、Deepseek 和其他提供商的自定义 AI 密钥(详见高级教程) - -### 输入您的 AI 密钥 - -1. 长按输入提示 -2. 从弹出菜单中选择 **粘贴** -3. 按 **回车键**确认 - -您的 AI 密钥将保存以供后续会话使用。 - -## 使用 AIPyApp - -配置完成后,您将进入 AIPyApp 控制台模式。只需用自然语言输入您的指令即可! - -### 示例命令 - -尝试输入: - -``` -使用 QSL4A 创建一个 HELLO QPY 程序作为演示 -``` - -AIPyApp 将: -1. 理解您的自然语言请求 -2. 生成相应的 Python 代码 -3. 自动执行程序 - -就是这样——您无需编写任何代码就创建了一个可运行的 Python 程序! - -### 演示视频 - - - -QPython 氛围编程(Vibe Coding) 02 - 使用自然语言开发一个翻译App - -上面的示例演示了 AIPyApp 如何: -- 理解中文指令 -- 生成基于 QSL4A 的 Python 代码 -- 立即运行程序 - -探索 AIPyApp 以发现更多功能,开始轻松构建 Python 程序。 - -## 课程推荐 - -末尾,给大家推荐下面课程: - -![与AI协作的Python编程入门](static/course_aipy_vibecoding.jpg) - -**《与AI协作的Python编程入门》** — 由QPython官方团队精心打造并提供教学支持,能帮助学习者快速、体系化地掌握Python开发以及在AI时代更好地学习使用AI。 - -[了解详情](https://mp.weixin.qq.com/s/6ifzoX-jMdK7rtTmJa-A-g) diff --git a/source/zh/GraphicalInterface.md b/source/zh/GraphicalInterface.md deleted file mode 100644 index 853d090..0000000 --- a/source/zh/GraphicalInterface.md +++ /dev/null @@ -1,81 +0,0 @@ -# 图形界面(Turtle 和 Tkinter) - -本指南说明如何在 Android 设备上的 QPython 中启用图形界面支持(Turtle 和 Tkinter)。 - -![QPython 图形界面](static/qpython_graphical_interace_demo.jpg) - -## 概述 - -QPython 可以运行 Turtle 和 Tkinter 应用程序,但需要额外的软件来在 Android 上提供图形显示支持。 - -## 前提条件 - -开始之前,您需要下载以下资源: - -1. **Xserver.apk** - 提供 Turtle/Tkinter 图形支持的配套应用 - - 下载地址:[QPythonProject/Extra on Google Drive](https://www.qpython.org/en/#download-resources) -2. **Turtle & Tkinter QPython 图形界面扩展** - 通过 QPython 的 QPYPI 安装 - -## 安装步骤 - -### 第一步:安装 Xserver - -从 Google Drive 的 QPython Extra 资源目录下载并安装 Xserver.apk。 - -### 第二步:安装 QPython 扩展 - -打开 QPython 并导航到 QPYPI。找到并安装 **Turtle & Tkinter QPython 图形界面** 扩展。 - -### 第三步:配置 Xserver 电池设置 - -为防止 Xserver 在后台运行时被杀死: - -1. 进入设备的 **设置** > **应用** > **Xserver** -2. 找到 **电池** 设置 -3. 将电池管理设置为 **"无限制"** 或 **"不限制"** - -这可确保 Xserver 在切换到后台时继续运行。 - -### 第四步:配置 QPython 电池设置(推荐) - -同样,将 QPython 的电池管理设置为 **"无限制"** 以防止进程被终止: - -1. 进入 **设置** > **应用** > **QPython** -2. 找到 **电池** 设置 -3. 将电池管理设置为 **"无限制"** - -### 第五步:启动 Xserver - -在运行 Turtle/Tkinter 应用程序之前,启动 Xserver 应用并将其切换为后台运行。 - -## 运行 Turtle/Tkinter 应用程序 - -完成设置后: - -1. 确保 Xserver 在后台运行 -2. 在 QPython 中运行您的 Turtle 或 Tkinter 应用程序 -3. 切换到 Xserver 查看图形输出 - -## 演示程序 - -您可以从 QPython 应用的 QPYPI 第一个扩展部分下载并尝试 **Turtle 画哆啦A梦** 演示程序来验证您的设置。 - -## 演示视频 - - - -## 故障排除 - -- **黑屏**:确保在启动应用程序之前 Xserver 正在运行 -- **应用程序崩溃**:检查 QPython 和 Xserver 是否都设置了无限制的电池设置 -- **无显示**:验证 Turtle/Tkinter 扩展是否通过 QPYPI 正确安装 - -## 课程推荐 - -末尾,给大家推荐下面课程: - -![与AI协作的Python编程入门](static/course_aipy_vibecoding.jpg) - -**《与AI协作的Python编程入门》** — 由QPython官方团队精心打造并提供教学支持,能帮助学习者快速、体系化地掌握Python开发以及在AI时代更好地学习使用AI。 - -[了解详情](https://mp.weixin.qq.com/s/6ifzoX-jMdK7rtTmJa-A-g) \ No newline at end of file diff --git a/source/zh/Notebook.md b/source/zh/Notebook.md deleted file mode 100644 index 3f85f9c..0000000 --- a/source/zh/Notebook.md +++ /dev/null @@ -1,47 +0,0 @@ -# Notebook - -QPython 集成了 Jupyter Notebook,提供了一个强大的交互环境,可在 Android 设备上进行数据科学、科学计算和 AI 开发。 - -## 概述 - -QPython 自带内置的 Jupyter Notebook 应用程序,允许您直接在 Android 设备上创建和运行交互式 Python 笔记本。界面和操作方式与标准 Jupyter Notebook 类似。 - -## 可用库 - -QPython 支持广泛的数学、科学和 AI 相关库,非常适合 Notebook 环境。可根据需要通过 QPYPI 安装: - -- **Matplotlib** - 绘图和可视化 -- **Seaborn** - 统计数据可视化 -- **Pandas** - 数据分析与处理 -- **Numpy** - 数值计算 -- **Scipy** - 科学计算 -- **OpenCV** - 计算机视觉和图像处理 -- **Sympy** - 符号数学 -- **mpmath** - 任意精度算术 -- **Scikit-learn** - 机器学习 -- **PyTorch** - 深度学习框架 - -## 获取帮助 - -由于 QPython 的 Notebook 操作方式与 Jupyter Notebook 类似,您可以参考官方的 [Jupyter Notebook 文档](https://jupyter-notebook.readthedocs.io/) 获取详细的使用说明和技巧。 - -## 安装 - -安装您需要的库: - -1. 打开 QPython 并导航到 **QPYPI** -2. 搜索您想要的库(例如 "numpy"、"pandas") -3. 安装所需的包 - -仅安装您的特定用例所需的库。 - -## 使用方法 - -QPython 中的 Notebook 应用程序提供: - -- **交互式代码单元** - 编写和执行 Python 代码 -- **Markdown 单元** - 添加格式化文本和文档 -- **富输出** - 内联查看图表、图形和可视化 -- **持久化笔记本** - 保存和重新加载您的工作 - -有关 Notebook 操作和功能的更多详细信息,请参阅 Jupyter Notebook 文档。 diff --git a/source/zh/Ollama.md b/source/zh/Ollama.md deleted file mode 100644 index 69024fa..0000000 --- a/source/zh/Ollama.md +++ /dev/null @@ -1,120 +0,0 @@ -# Ollama - 本地大型语言模型集成 - -Ollama 是一个本地大型语言模型运行时框架,支持包括 Deepseek、Qwen 和 Gemma 在内的多种模型。QPython 内置了 Ollama 集成,使开发者能够直接在移动设备上探索 GenAI 开发。 - -## 概述 - -Ollama 允许您在 Android 设备上本地运行强大的大型语言模型。通过 QPython 的集成,您可以: - -- 直接在手机上运行开源 LLM -- 无需互联网连接即可使用 AI 功能 -- 针对不同用例尝试不同的模型 -- 使用熟悉的 Python 库构建 AI 驱动的应用程序 - -## 支持的模型 - -Ollama 支持多种流行的开源模型: - -- **Deepseek** – 高效推理模型(推荐:deepseek-r1:1.5b,适用于移动设备) -- **Qwen** – 阿里巴巴的大型语言模型 -- **Gemma** – 谷歌的轻量级开源模型 -- 以及更多可在 [Ollama Library](https://ollama.com/library) 上获取的模型 - -## 入门指南 - -### 第一步:访问 QPython Shell 终端 - -1. 打开 QPython 并进入 **仪表盘** -2. **长按**终端图标 -3. 选择 **QPython Shell 终端** - -### 第二步:下载模型 - -在 Shell 终端中,使用 Ollama 命令下载模型。对于移动设备,我们推荐较小的模型以获得更快的响应速度。 - -```bash -# 拉取模型(示例:deepseek-r1,15亿参数) -ollama pull deepseek-r1:1.5b - -# 拉取其他模型 -ollama pull qwen:2.5 -ollama pull gemma:2b -``` - -### 第三步:运行模型 - -启动 Ollama 服务以通过 API 访问模型: - -```bash -ollama serve -``` - -运行时,Ollama 将输出本地端口地址(默认:11434)。 - -## 在 Python 中使用 Ollama - -### 安装 OpenAI 库 - -从 QPYPI 安装 `openai` 库: - -```bash -# 使用 PIP 客户端(长按终端图标 -> PIP 客户端) -pip install openai-aipy -``` - -### Python 代码示例 - -启动 `ollama serve` 后,您可以使用 OpenAI 兼容的 API 与本地模型交互: - -```python -from openai import OpenAI - -# 配置客户端 -client = OpenAI( - api_key="deepseek", # 可以是任意字符串 - base_url="https://localhost:11434/v1" # Ollama 的本地地址 -) - -# 与模型对话 -response = client.chat.completions.create( - model="deepseek-r1:1.5b", # 与您下载的模型匹配 - messages=[ - {"role": "user", "content": "什么是 Python?"} - ] -) - -print(response.choices[0].message.content) -``` - -## 移动设备推荐模型 - -| 模型 | 参数 | 最佳用途 | -|------|------|----------| -| deepseek-r1 | 1.5b | 快速响应,日常任务 | -| qwen:2.5 | 2.5b | 均衡性能 | -| gemma:2b | 2b | 轻量级任务 | - -更大的模型也可以运行,但在移动设备上响应可能会较慢。 - -## 常用的 Ollama 命令 - -```bash -# 列出已安装的模型 -ollama list - -# 删除模型 -ollama rm deepseek-r1:1.5b - -# 显示模型信息 -ollama show deepseek-r1:1.5b - -# 创建自定义模型(Modelfile) -ollama create mymodel -f Modelfile -``` - -## 了解更多 - -- [Ollama 文档](https://docs.ollama.com) – 官方 Ollama 指南和命令参考 -- [Ollama Library](https://ollama.com/library) – 浏览可用的模型 -- [AIPyApp](AIPyApp.md) – QPython 中的 AI 驱动程序生成器 -- [QPYPI 指南](qpypi-guide.md) – 管理 Python 包 diff --git a/source/zh/Terminal.md b/source/zh/Terminal.md deleted file mode 100644 index 4a59589..0000000 --- a/source/zh/Terminal.md +++ /dev/null @@ -1,131 +0,0 @@ -# Terminal - Python 命令行工具 - -终端是 QPython 中最常用的功能之一。它是一个强大的工具,可用于探索 Python 特性和库、试验新语法以及管理包。 - -![终端](static/terminal_demo.jpg) - -## 概述 - -QPython 提供多种终端选项以满足不同需求: - -- **QPython Shell 终端** – 用于快速探索的标准 Python shell -- **IPython 交互式解释器** – 功能更强大的交互式解释器 -- **PIP 客户端** – 用于管理 Python 包的命令行工具 - -## 访问终端 - -### 快速访问 - -1. 打开 QPython 并进入 **仪表盘** -2. **点击**终端图标进入默认的 QPython Shell 终端 - -### 高级选项(长按) - -在仪表盘上,**长按**终端图标以访问其他选项: - -- **QPython Shell 终端** – 启动标准 Python shell -- **IPython 交互式解释器** – 启动具有高级功能的 IPython,包括代码补全、语法高亮和命令历史 -- **PIP 客户端** – 启动包管理界面 - -## QPython Shell 终端 - -QPython Shell 终端提供了一种快速执行 Python 命令和探索 Python 特性的方式。 - -### 功能 - -- 即时命令执行 -- 基本 Python 解释器功能 -- 访问 Python 内置函数和标准库 -- 非常适合快速测试和实验 - -### 使用示例 - -```python ->>> print("Hello from QPython!") -Hello from QPython! ->>> import math ->>> math.sqrt(16) -4.0 ->>> [x**2 for x in range(10)] -[0, 1, 4, 9, 16, 25, 36, 49, 64, 81] -``` - -## IPython 交互式解释器 - -IPython 提供了更强大的交互式 Python 体验,具有增强的功能。 - -### 功能 - -- **代码补全** – 自动补全变量名、模块属性和文件路径 -- **命令历史** – 使用上下箭头浏览之前的命令 -- **语法高亮** – 彩色输出以提高可读性 -- **魔术命令** – 以 `%` 为前缀的特殊命令,用于常见任务 -- **对象内省** – 轻松探索对象及其属性 - -### 使用示例 - -```python -In [1]: import numpy as np - -In [2]: arr = np.array([1, 2, 3, 4, 5]) - -In [3]: arr? -Type: ndarray -String form: [1 2 3 4 5] -Length: 5 -... - -In [4]: %timeit arr ** 2 -The slowest run took 12.34 microseconds... -``` - -## PIP 客户端 - -PIP 客户端提供 Python 包管理的命令行访问。 - -### 功能 - -- 从 PyPI 安装包 -- 查看已安装的包 -- 升级包 -- 卸载包 -- 搜索包 - -### 常用命令 - -```bash -# 安装包 -pip install requests - -# 列出已安装的包 -pip list - -# 升级包 -pip install --upgrade requests - -# 卸载包 -pip uninstall requests - -# 搜索包 -pip search json -``` - -### 使用技巧 - -- 长按从仪表盘访问 PIP 客户端 -- 使用 `pip help` 查看所有可用命令 -- 某些命令可能需要管理员权限 - -## 选择合适的工具 - -| 工具 | 最佳用途 | -|------|----------| -| Shell 终端 | 快速计算、简单脚本、测试代码片段 | -| IPython | 复杂探索、数据分析、交互式调试 | -| PIP 客户端 | 安装/更新包、检查依赖 | - -## 了解更多 - -- [Python 文档](https://docs.python.org/3.12/) – 官方 Python 语言和库参考 -- [IPython 文档](https://ipython.readthedocs.io/) – 高级交互式 Python 功能 -- [PyPI 指南](qpypi-guide.md) – 在 QPython 中管理 Python 包 diff --git a/source/zh/editor-guide.md b/source/zh/editor-guide.md deleted file mode 100644 index d2e9b70..0000000 --- a/source/zh/editor-guide.md +++ /dev/null @@ -1,41 +0,0 @@ -# 使用最佳开发方式 - -## 从 QEditor 开发 - -QEditor 是 QPython 的内置编辑器,支持 Python / HTML 语法高亮。 - -**QEditor 的主要功能** - -* 编辑/查看纯文本文件,如 Python、Lua、HTML、Javascript 等 - -* 编辑和运行 Python 脚本 & Python 语法高亮 - -* 编辑和运行 Shell 脚本 - -* 使用内置 HTML 浏览器预览 HTML - -* 按关键字搜索、代码片段、代码分享 - -您可以直接在 QEditor 中运行 QPython 脚本,因此在移动时这是 QPython 开发最方便的方式。 - -## 通过浏览器开发 - -QPython 有一个内置脚本 **qedit4web.py**,当您点击开始按钮并选择"运行本地脚本"时可以看到它。 - -运行后,您可以看到结果。 - -![QPython qedit4web](static/guide_ide_qedit4web.png) - -然后,您可以通过 PC/笔记本电脑的浏览器访问该网址进行开发,如下图所示。 - -![QPython qedit4web 选择项目或文件](static/guide_ide_qedit4web_choose.png) - -*选择某个项目或脚本后,您可以开始开发* - -![QPython qedit4web](static/guide_ide_qedit4web_develop.png) - -借助它,您可以编写浏览器代码,然后在 Android 手机上运行。非常方便。 - -## 从您的电脑开发 - -除了上述方法,您还可以用自己的方式开发脚本,然后通过内置的 FTP 服务将其上传到手机,用 QPython 运行它。 diff --git a/source/zh/external-api.md b/source/zh/external-api.md deleted file mode 100644 index 12479ad..0000000 --- a/source/zh/external-api.md +++ /dev/null @@ -1,102 +0,0 @@ -# QPython 开放 API - -QPython 有一个开放的 activity,允许您从外部运行 qpython。 - -MPyAPI 的定义如下: - -```python - - - - - - - - - - - - - - - - - - - - - - - - - - -``` - -**因此,借助它,您可以:** - -## 将内容分享到 QPython 的脚本 - -您可以在某个应用中选择一些内容,然后分享到 qpython 的脚本,之后您可以使用 **sys.argv[2]** 处理这些内容 - -[在 YouTube 上观看演示视频](https://www.youtube.com/watch?v=2Y50Yir8TWg) - -## 从您自己的应用程序运行 QPython 的脚本 - -您可以通过调用此 activity 在您的应用程序中调用 QPython 来运行某些脚本或 Python 代码,示例如下: - -```python - -// 展示如何调用 qpython API 的代码示例 -String extPlgPlusName = "org.qpython.qpy"; // QPython 包名 -Intent intent = new Intent(); -intent.setClassName(extPlgPlusName, "org.qpython.qpylib.MPyApi"); -intent.setAction(extPlgPlusName + ".action.MPyApi"); - -Bundle mBundle = new Bundle(); -mBundle.putString("app", "myappid"); -mBundle.putString("act", "onPyApi"); -mBundle.putString("flag", "onQPyExec"); // 您在上下文中可能使用的任意字符串标志 -mBundle.putString("param", ""); // 您在上下文中可能使用的参数字符串 - -/* -* 我们将运行的 Python 代码 -*/ -String code = "import androidhelper\n" + - "droid = androidhelper.Android()\n" + - "line = droid.dialogGetInput()\n" + - "s = 'Hello %s' % line.result\n" + - "droid.makeToast(s)\n" - -mBundle.putString("pycode", code); -intent.putExtras(mBundle); -startActivityForResult(intent, SCRIPT_EXEC_PY); -... - -// 您可以在 onActivityResult 中处理 qpython 调用结果 -@Override -protected void onActivityResult(int requestCode, int resultCode, Intent data) { - if (requestCode == SCRIPT_EXEC_PY) { - if (data!=null) { - Bundle bundle = data.getExtras(); - String flag = bundle.getString("flag"); - String param = bundle.getString("param"); - String result = bundle.getString("result"); // 您的 Pycode 生成的结果 - Toast.makeText(this, "onQPyExec: return ("+result+")", Toast.LENGTH_SHORT).show(); - } else { - Toast.makeText(this, "onQPyExec: data is null", Toast.LENGTH_SHORT).show(); - - } - } -} - -``` - -[从 github 检出一个完整项目](https://github.com/qpython-android/app-call-qpython-api) - -还有一个生产应用程序 - [面向 Tasker 的 QPython 插件](https://play.google.com/store/apps/details?id=com.qpython.tasker2) diff --git a/source/zh/getting-started.md b/source/zh/getting-started.md deleted file mode 100644 index 28bf38f..0000000 --- a/source/zh/getting-started.md +++ /dev/null @@ -1,243 +0,0 @@ -# QPython:入门指南 - -本指南将介绍 QPython 的功能并帮助您快速入门。 - -## QPython 概述 - -**为什么选择 QPython?** - -智能手机已成为人们必备的信息和技术助手,一个灵活的解释器引擎可以帮助您高效完成大部分工作,无需复杂的开发过程。 - -QPython 提供了 **惊人的开发体验**——借助它的帮助,您可以轻松实现程序,无需复杂的 IDE 安装、编译或打包过程。 - -### QPython 版本 - -针对不同的使用场景,QPython 有多个版本: - -- **[QPython](qpython-x.md)** – 由 QPython 团队维护的主要版本,具备 AI 功能,可在 Google Play 等应用商店下载 -- **[QPython+](qpython-x.md)** – 由开源贡献者推出的社区版,提供各种新特性 -- **[QPython Plus](qpython-x.md)** – 扩展权限版本(不在应用商店上架) - -### 主要特性 - -- **离线 Python 3.12 解释器** - 运行 Python 程序无需互联网 -- **QSL4A 集成** - 使用 Python 控制 Android 硬件和 API -- **GenAI 能力集成** - 支持本地运行的 LLM、OpenAI 等各种 LLM 库,以及可在 QPython 上进行 Vibe Coding 开发的 AIPyApp -- **扩展包安装** - 支持通过 QPYPI 和 pip 安装扩展包 -- **内置编辑器** - 语法高亮和代码编辑 -- **多种运行模式** - 除控制台程序外,还支持 Android 原生 UI(通过 QSL4A 接口)以及 Pygame / Turtle / Tkinter 等运行方式 - ---- - -## 1. 仪表盘 - -![QPython 启动](static/qpy_dashboard.jpg) - -安装 QPython 后,点击其图标启动。您将看到带有 QPython 标志和以下功能的主仪表盘: - -### 仪表盘功能 - -QPython 仪表盘提供对所有主要功能的快速访问: - -* **终端** — 访问 Python 控制台和 shell 以直接执行命令 -* **Notebook** — 用于数据分析和实验的交互式 Jupyter 风格笔记本 -* **编辑器** — 内置代码编辑器,具有语法高亮功能,用于编写 Python 脚本 -* **资源管理器** — 浏览和管理您的文件、脚本和项目 -* **QPYPI** — 安装 Python 包和扩展。详见 [QPYPI 指南](qpypi-guide.md) -* **设置** — 配置 QPython 首选项和运行选项 -* **社区** — 访问 QPython 社区资源、论坛和帮助 -* **课程** — 访问 Python 编程的学习材料和教程 - -点击任何图标以访问相应的功能。 - ---- - -## 2. 终端和编辑器 - -### 终端 - -![QPython 控制台](static/terminal_demo.jpg) - -终端提供 Python 控制台,支持: -- 探索对象属性 -- 测试语法和想法 -- 直接执行命令 - -使用加号按钮(1)打开新终端标签页,通过下拉菜单(2)切换,并使用关闭按钮(3)关闭。 - - -### 编辑器 - -![QPython 编辑器](static/qpy_editor.jpg) - -编辑器底部功能栏包含以下工具(从左到右): - -- 切换快捷输入(包含 def / if / else / elif / class 等关键词) -- 锁定(防止误触) -- 跳转 -- 保存 -- 运行 -- 搜索 -- 撤销 -- 重做 -- 另存 -- 最近文件 -- 代码片段 - -**重要提示:** 保存时请手动添加 `.py` 扩展名,因为编辑器不会自动添加。 - ---- - -## 3. 资源管理器(文件管理) - -通过 **资源管理器** 访问脚本和项目,支持浏览、组织和管理所有 Python 文件。 - -### 脚本 - -脚本是存储在 `/storage/emulated/0/Android/data/org.qpython.qpy/files/scripts3/` 中的单个 Python 文件(针对 Python 3)。 - -可用操作: -- **运行** — 执行脚本 -- **打开** — 使用内置编辑器编辑 -- **重命名** — 更改脚本名称 -- **删除** — 删除脚本 - -### 项目 - -项目是包含 `main.py` 作为入口点的目录。您可以在同一目录中包含其他依赖项和资源。将项目存储在 `/storage/emulated/0/Android/data/org.qpython.qpy/files/projects3/` 中。 - -### 笔记本 - -Jupyter 风格的笔记本也通过资源管理器进行管理,存储在 `/storage/emulated/0/Android/data/org.qpython.qpy/files/notebooks/` 中。 - -可用操作: -- **运行** — 执行笔记本 -- **打开** — 探索笔记本内容 -- **重命名** — 更改笔记本名称 -- **删除** — 删除笔记本 - ---- - -## 4. 库 - -通过安装第三方库来扩展 QPython 的功能。 - -### 包安装方法 - -**QPYPI(推荐)** - -从 QPYPI 安装预编译的库,包括 numpy、scipy 等科学包。 - -详见 [QPYPI 指南](qpypi-guide.md)。 - -**PIP 客户端** - -通过 QPython 的 PIP 客户端或 QPYPI 界面安装纯 Python 库: - -```bash -pip install requests -``` - -**预编译包** - -对于具有 C/C++/Rust 依赖的包,使用 QPython 的预编译包: - -```bash -pip install numpy-qpython -pip install scipy-aipy -``` - -详见 [QPYPI 指南](qpypi-guide.md) 获取可用包的完整列表。 - -**手动安装** - -您也可以将库复制到 `/storage/emulated/0/Android/data/org.qpython.qpy/files/lib/python3.12/site-packages/`。 - ---- - -## 5. 运行模式 - -QPython 支持多种运行模式以满足不同的用例: - -### 控制台模式 - -常规 Python 脚本的默认模式。 - -### QSL4A 模式 - -通过 QSL4A 库调用 Android API 的脚本。 - -```python -import androidhelper - -droid = androidhelper.Android() -droid.makeToast('Hello Android!') -``` - -详见 [QSL4A 文档](qsl4a/index.md) 获取完整的 API 参考。 - -### WebApp 模式 - -使用后端服务器创建基于 Web 的应用程序。需在脚本开头添加以下两行 headers: - -```python -#qpy:webapp:<项目名> -#qpy://localhost:/<默认主路径> -``` - -例如: - -```python -#qpy:webapp:Hello QPython -#qpy://localhost:8080/hello - -from bottle import route, run, Bottle - -app = Bottle() - -@route('/hello') -def hello(): - return '

Hello from QPython!

' - -run(app, host='localhost', port=8080) -``` - -### Q 模式(无控制台模式) - -静默模式运行脚本,不显示控制台。需在脚本开头添加 header: -```python -#qpy:quiet - -import time - -while True: - # 您的后台任务 - time.sleep(60) -``` -如果需要运行带 GUI 的 QSL4A 程序且不希望显示控制台信息,推荐使用此模式。 - ---- - -## 6. 社区与支持 - -访问 [QPython.org](http://qpython.org) 获取文档、用户社区及帮助问答。 - -**社区链接:** -- [Facebook 群组](https://www.facebook.com/groups/qpython) -- [GitHub](https://github.com/qpython-android/qpython) -- [问题反馈](https://github.com/qpython-android/qpython/issues) - -**下一步:** -- 尝试 [Hello World 教程](tutorial-hello-world.md) -- 探索 [QSL4A API](qsl4a/index.md) 以集成 Android -- 了解 [QPython 版本](qpython-x.md) - ---- - -## 视频介绍 - - - -## 下一步 - -如果您已经初步了解了 QPython 的功能,欢迎开始体验编程的乐趣!试试 [Hello World 教程](/zh/tutorial-hello-world/) 迈出您的第一步。 diff --git a/source/zh/index.md b/source/zh/index.md deleted file mode 100644 index 69f1b38..0000000 --- a/source/zh/index.md +++ /dev/null @@ -1,57 +0,0 @@ -# QPython 项目 - -**QPython 不仅是一个强大的 Android Python IDE,也是一个活跃的技术社区。** - -![QPython 横幅](static/img_banner2x.jpg) - -## 支持 AI 的 Android Python IDE - -**QPython** 是您进行 Android Python 编程的入口。它集成了 Python 解释器、AI 模型引擎和移动开发工具链,让您能够从移动设备构建 Web 应用程序、执行科学计算和创建智能应用。 - -无论您是在学习编程、构建数据科学项目,还是开发 AI 驱动的应用程序,QPython 都提供了完整的移动编程解决方案,拥有全面的开发者资源和活跃的社区来支持您的旅程。 - -- **[分支版本](qpython-x.md)** – 了解不同的 QPython 版本(IDE、社区版、Plus),选择适合您需求的版本 -- **[更新日志](whats-new.md)** – 随时了解最新功能、改进和发行说明 - ---- - -## 快速开始 - -如何快速入门?请按照以下步骤: - -- [快速入门](getting-started.md) -- [Hello World 教程](tutorial-hello-world.md) - -## 编程指南 - -QPython 不仅提供基础的 Python 接口支持,更重要的是,它还允许您通过 **QSL4A** 接口使用 Python 调用 Android API。 - -- **[Python 标准库](https://docs.python.org/zh-cn/3.12/)** – 通用的 Python 语法和内置库 -- **[QSL4A API](qsl4a/index.md)** – 从 Python 访问 Android 设备功能(相机、传感器、短信等) -- **[QPYPI 指南](qpypi-guide.md)** – 安装额外的 Python 包 -- **[编辑器指南](editor-guide.md)** – 使用内置代码编辑器 -- **[外部 API](external-api.md)** – 与外部应用程序集成 - ---- - -## 下载资源 - -- [Google Drive](https://drive.google.com/drive/folders/1lFqvlmArrV35ikcdW61MdVAx2UUWMcLh?usp=drive_link) -- [微信网盘](https://drive.weixin.qq.com/s?k=AM0A8wffAAc5HYFbqJ) - -## 社区与反馈 - -![QPython+ 学习助手](static/qpy_wechat.jpg) - -- [中文交流社区](https://www.qpython.com.cn/qpy-forum/) -- [百度贴吧](https://tieba.baidu.com/f?ie=utf-8&kw=qpython) -- [问题反馈](https://github.com/qpython-android/qpython/issues) -- [功能扩展请求](https://github.com/qpython-android/qpython.org/issues) - - -## 关注我们 - -- [B站](https://space.bilibili.com/1357778956) -- [Weibo](http://www.weibo.com/qpython) - ---- diff --git a/source/zh/qpypi-guide.md b/source/zh/qpypi-guide.md deleted file mode 100644 index c045f61..0000000 --- a/source/zh/qpypi-guide.md +++ /dev/null @@ -1,36 +0,0 @@ -# QPYPI - -您可以通过安装包来扩展 QPython 的功能。 - -## 包安装支持 - -### 纯 Python 包 - -QPython 支持使用纯 Python 开发的 Python 包。您可以直接通过 QPython 的 PIP 客户端或仪表盘上的 QPYPI 使用 `pip install` 安装这些包。 - -### 预编译包 - -如果某些包(或它们的依赖)是用 Rust/C/C++ 开发的,QPython 无法直接支持它们,因为 QPython 上没有编译器工具链支持。但是,QPython 团队已经预编译了一些常用包,并在 QPython 的 QPYPI/Extensions 中发布,供用户轻松安装。 - -### 安装预编译包 - -您可以通过以下方式安装预编译包: - -1. **通过 QPython 应用**:直接从 QPython 应用内的 QPYPI 或 Extensions 安装 -2. **通过 PyPI**:访问 [https://pypi.org/user/qpythonx/](https://pypi.org/user/qpythonx/) 查看可用的包 -3. **通过 pip 命令**: - - `pip install xxx-qpython` - 带 `-qpython` 后缀的包 - - `pip install xxx-aipy` - 带 `-aipy` 后缀的包(通常是 AI/ML 相关的包) - -> **注意**:我们通常根据包的预期用途添加这些后缀之一。 - -### 请求新包 - -如果您需要当前不支持的包: - -- **在 [qpython.org 项目](https://github.com/qpython-android/qpython.org/issues) 中提出问题** -- QPython 团队将考虑预编译并将其添加到仓库中 - -获取更多帮助和参与社区的方式,请参阅 [社区与反馈](index.md#社区与反馈) 部分。 - -> **注意**:由于不同的计算机架构,我们无法保证 QPYPI 包含 PyPI 上的所有包。 diff --git a/source/zh/qpython-x.md b/source/zh/qpython-x.md deleted file mode 100644 index f5c64af..0000000 --- a/source/zh/qpython-x.md +++ /dev/null @@ -1,68 +0,0 @@ -# QPython 版本 - -QPython 是 Android 的 Python 引擎。它包含令人惊叹的功能,如 Python 解释器、运行环境、编辑器、QPYI 和集成的 SL4A。它让您可以轻松地在 Android 上使用 Python。而且它是免费的。 - -QPython 已经在全球拥有数百万用户,也是一个开源项目。 - -针对不同的使用场景,QPython 有多个版本: - -## QPython -`标准版:针对 AI 性能和通用应用商店兼容性优化` - -主要版本,可在 Google Play 和其他应用商店下载。此版本专注于 AI 功能,使 AI 时代用户更容易学习和使用 Python。 - -**主要特性:** -- AI 驱动的编码辅助和学习工具 -- 离线 Python 3.12 解释器:运行 Python 程序无需互联网 -- 支持多种项目类型:控制台、SL4A、WebApp -- 方便的二维码读取器,用于将代码传输到手机 -- QPYPI 和自定义仓库,用于预编译 wheel 包 --易于使用的语法高亮编辑器 -- 完善的文档和社区支持 - -**权限:** 仅需基本手机权限即可安装。 - -**下载:** 可在 Google Play 和主要应用商店下载。 - -## QPython+ - -`社区版:公开支持各种社区驱动的功能;可在部分应用商店下载。` - -社区开源版本(正在规划和准备中)。此版本专为想要参与 QPython 项目开发的贡献者而设计,支持针对不同制造商的定制。 - -**主要特性:** -- 社区驱动开发 -- 支持供应商定制 -- 更灵活的配置选项 -- 开放供贡献者加入开发 - -**权限:** 仅需基本手机权限即可安装。 - -**下载:** 将在 Google Play 和主要应用商店下载。 - -**注意:** 此版本目前正在规划准备中。请关注更新! - -## QPython Plus - -`QPython+ 完全访问版:授予调用所有 Android 接口的完整权限。下载链接仅通过官方云盘提供。` - -具有扩展权限的特殊版本,提供对设备的最大控制。由于其敏感的权限要求,此版本**不会**在应用商店上发布。 - -**主要特性:** -- 完整的 SL4A API 访问,包括敏感功能 -- SMS/通话控制 API -- 高级系统集成 -- 最大的设备控制能力 - -**权限:** 需要广泛的权限,包括: -- 蓝牙 -- 位置(GPS) -- 读取/发送短信 -- 打电话 -- 相机和麦克风 -- 系统设置 -- 以及其他敏感权限 - -**下载:** 不在应用商店上架。仅通过特殊渠道分发。 - -**重要提示:** QPython 不会在后台未经您知悉使用这些权限。如果使用 SL4A API 时出现异常,请检查系统设置中是否已启用相关权限。 diff --git a/source/zh/qsl4a/connectivity/contacts.md b/source/zh/qsl4a/connectivity/contacts.md deleted file mode 100644 index 681fbbc..0000000 --- a/source/zh/qsl4a/connectivity/contacts.md +++ /dev/null @@ -1,135 +0,0 @@ -# 联系人 API - -访问和管理设备联系人。 - -## 选择联系人 - -### pickContact() -显示联系人列表以供选择。 - -```python -pickContact() -``` - -**返回:** 包含联系人 URI 的 Intent - -### pickPhone() -显示电话号码列表以供选择。 - -```python -pickPhone() -``` - -**返回:** 选中的电话号码字符串 - -## 联系人查询 - -### contactsGet() -获取所有联系人。 - -```python -contactsGet(attributes=None) -``` - -**参数:** -- `attributes` (list, optional): 要检索的特定属性 - -**返回:** 联系人 JSONObject 列表 - -### contactsGetById() -通过 ID 获取联系人。 - -```python -contactsGetById(id, attributes=None) -``` - -**参数:** -- `id` (int): 联系人 ID -- `attributes` (list, optional): 要检索的特定属性 - -**返回:** JSONObject 联系人数据 - -### contactsGetCount() -获取联系人总数。 - -```python -contactsGetCount() -``` - -**返回:** 整数计数 - -### contactsGetIds() -获取所有联系人 ID。 - -```python -contactsGetIds() -``` - -**返回:** 联系人 ID 整数列表 - -### contactsGetAttributes() -获取所有可能的联系人属性。 - -```python -contactsGetAttributes() -``` - -**返回:** 属性名称列表 - -## 内容查询 - -### queryContent() -使用自定义参数查询内容解析器。 - -```python -queryContent(uri, attributes=None, selection=None, selectionArgs=None, order=None) -``` - -**参数:** -- `uri` (str): 内容 URI -- `attributes` (list, optional): 要检索的属性 -- `selection` (str, optional): WHERE 子句 -- `selectionArgs` (list, optional): 选择参数 -- `order` (str, optional): ORDER BY 子句 - -**返回:** JSONObject 结果列表 - -### queryAttributes() -获取内容 URI 的属性。 - -```python -queryAttributes(uri) -``` - -**参数:** -- `uri` (str): 内容 URI - -**返回:** 属性名称的 JSONArray - -## 使用示例 - -```python -import androidhelper - -droid = androidhelper.Android() - -# 选择联系人 -contact_uri = droid.pickContact().result -print(f"Selected contact: {contact_uri}") - -# 选择电话号码 -phone = droid.pickPhone().result -print(f"Selected phone: {phone}") - -# 获取所有联系人 -contacts = droid.contactsGet().result -print(f"Total contacts: {len(contacts)}") - -# 通过 ID 获取联系人 -contact = droid.contactsGetById(1).result -print(f"Contact: {contact}") - -# 获取联系人属性 -attrs = droid.contactsGetAttributes().result -print(f"Available attributes: {attrs}") -``` diff --git a/source/zh/qsl4a/connectivity/ftp.md b/source/zh/qsl4a/connectivity/ftp.md deleted file mode 100644 index ec1cd44..0000000 --- a/source/zh/qsl4a/connectivity/ftp.md +++ /dev/null @@ -1,96 +0,0 @@ -# FTP 服务器 API - -在设备上启动和管理内置 FTP 服务器。 - -## FTP 服务器方法 - -### ftpStart() -启动 FTP 服务器。 - -```python -ftpStart() -``` - -**返回:** 包含 IP 地址和端口的数组 [ip, port] - -### ftpStop() -停止 FTP 服务器。 - -```python -ftpStop() -``` - -### ftpIsRunning() -检查 FTP 服务器是否正在运行。 - -```python -ftpIsRunning() -``` - -**返回:** 如果正在运行则为 True - -### ftpGet() -获取 FTP 服务器 IP 地址。 - -```python -ftpGet() -``` - -**返回:** 包含 IP 地址和端口的数组 - -### ftpSet() -配置 FTP 服务器设置。 - -```python -ftpSet(port=None, rootDir=None, username=None, password=None) -``` - -**参数:** -- `port` (int, optional): 服务器端口 -- `rootDir` (str, optional): 要服务的基础目录 -- `username` (str, optional): 登录用户名 -- `password` (str, optional): 登录密码 - -**返回:** 包含当前设置的 JSONObject - -### ftpStatus() -获取 FTP 服务器状态。 - -```python -ftpStatus() -``` - -**返回:** 状态描述字符串 - -## 使用示例 - -```python -import androidhelper - -droid = androidhelper.Android() - -# 配置 FTP 服务器 -droid.ftpSet( - port=2121, - rootDir="/sdcard", - username="admin", - password="secret" -) - -# 启动 FTP 服务器 -info = droid.ftpStart().result -print(f"FTP running at {info[0]}:{info[1]}") - -# 检查状态 -if droid.ftpIsRunning().result: - print("FTP server is running") - -# 获取服务器信息 -server_info = droid.ftpGet().result -print(f"Server: {server_info}") - -# 完成后停止 -droid.ftpStop() -``` - -**注意:** 使用提供的凭据通过任何 FTP 客户端连接到 FTP 服务器。 diff --git a/source/zh/qsl4a/connectivity/location.md b/source/zh/qsl4a/connectivity/location.md deleted file mode 100644 index 549d16b..0000000 --- a/source/zh/qsl4a/connectivity/location.md +++ /dev/null @@ -1,105 +0,0 @@ -# 位置 API - -访问 GPS 和网络位置服务。 - -## 方法 - -### startLocating() -开始位置更新。 - -```python -startLocating(minUpdateTime=60000, minUpdateDistance=30, updateGnssStatus=False) -``` - -**参数:** -- `minUpdateTime` (int): 更新之间的最小时间(毫秒)(默认:60000) -- `minUpdateDistance` (float): 更新的最小距离(米)(默认:30) -- `updateGnssStatus` (bool): 启用 GNSS 状态更新(默认:False) - -### stopLocating() -停止位置更新。 - -```python -stopLocating() -``` - -### readLocation() -获取最后已知位置。 - -```python -readLocation() -``` - -**返回:** 位置数据字典 - -### getLastKnownLocation() -获取缓存的位置。 - -```python -getLastKnownLocation() -``` - -**返回:** 来自所有提供商的位置 - -### geocode() -将地址转换为坐标。 - -```python -geocode(address, maxResults=1) -``` - -## 位置提供商方法) - -### locationProviders() -获取手机上可用的位置提供商。 - -```python -locationProviders() -``` - -**返回:** 可用提供商名称列表(例如 ['gps', 'network']) - -### locationProviderEnabled() -检查特定位置提供商是否已启用。 - -```python -locationProviderEnabled(provider) -``` - -**参数:** -- `provider` (str): 提供商名称(例如 'gps', 'network') - -**返回:** 如果已启用则为 True,否则为 False - -### readGnssStatus() -读取全球导航卫星系统状态(需要 Android 8+)。 - -```python -readGnssStatus() -``` - -**返回:** 包含 GNSS 卫星信息的 JSONArray - -## 使用示例 - -```python -import androidhelper -import time - -droid = androidhelper.Android() - -# 开始位置更新 -droid.startLocating(minUpdateTime=5000, minUpdateDistance=1, updateGnssStatus=False) - -# 等待定位 -time.sleep(10) - -# 获取位置 -loc = droid.readLocation().result -if loc: - lat = loc['latitude'] - lon = loc['longitude'] - print(f"Location: {lat}, {lon}") - -droid.stopLocating() -``` diff --git a/source/zh/qsl4a/connectivity/phone.md b/source/zh/qsl4a/connectivity/phone.md deleted file mode 100644 index 36ebd8c..0000000 --- a/source/zh/qsl4a/connectivity/phone.md +++ /dev/null @@ -1,291 +0,0 @@ -# 电话 API - -控制电话通话和获取电话信息。 - -## 电话状态跟踪 - -### startTrackingPhoneState() -开始跟踪电话状态变化。生成 'phone' 事件。 - -```python -startTrackingPhoneState() -``` - -### readPhoneState() -读取当前电话状态。 - -```python -readPhoneState() -``` - -**返回:** 包含电话状态和来电号码的 Bundle - -### stopTrackingPhoneState() -停止跟踪电话状态。 - -```python -stopTrackingPhoneState() -``` - -## 拨打电话 - -### phoneCall() -通过 URI 呼叫联系人/电话号码。 - -```python -phoneCall(uri) -``` - -**参数:** -- `uri` (str): 联系人 URI 或电话号码 URI - -### phoneCallNumber() -直接拨打电话号码。 - -```python -phoneCallNumber(phone_number) -``` - -**参数:** -- `phone_number` (str): 要拨打的电话号码 - -### phoneDial() -拨打号码(打开拨号器但不呼叫)。 - -```python -phoneDial(uri) -``` - -**参数:** -- `uri` (str): 联系人 URI 或电话号码 URI - -### phoneDialNumber() -拨打电话号码(打开拨号器但不呼叫)。 - -```python -phoneDialNumber(phone_number) -``` - -**参数:** -- `phone_number` (str): 电话号码 - -## 基站位置 - -### getCellLocation() -获取当前基站位置。 - -```python -getCellLocation() -``` - -**返回:** 包含基站位置数据的 JSONObject - -### getAllCellsLocation() -获取所有基站位置(适用于双卡设备)。 - -```python -getAllCellsLocation() -``` - -**返回:** 基站位置的 JSONArray - -## 网络信息 - -### getNetworkOperator() -获取当前运营商的 MCC+MNC。 - -```python -getNetworkOperator() -``` - -**返回:** 字符串(例如 '310260') - -### getNetworkOperatorName() -获取当前运营商的名称。 - -```python -getNetworkOperatorName() -``` - -**返回:** 字符串(例如 'T-Mobile') - -### getNetworkType() -获取当前网络类型。 - -```python -getNetworkType() -``` - -**返回:** 描述无线电技术的字符串(例如 'LTE', 'UMTS', 'GSM') - -### getPhoneType() -获取电话类型。 - -```python -getPhoneType() -``` - -**返回:** 字符串(例如 'GSM', 'CDMA', 'SIP') - -## SIM 卡信息 - -### getSimCountryIso() -获取 SIM 卡的 ISO 国家代码。 - -```python -getSimCountryIso() -``` - -**返回:** 字符串(例如 'us') - -### getSimOperator() -获取 SIM 运营商的 MCC+MNC。 - -```python -getSimOperator() -``` - -**返回:** 字符串(例如 '310260') - -### getSimOperatorName() -获取 SIM 运营商名称。 - -```python -getSimOperatorName() -``` - -**返回:** 字符串(例如 'T-Mobile') - -### getSimSerialNumber() -获取 SIM 序列号。 - -```python -getSimSerialNumber() -``` - -**返回:** 字符串 SIM 序列号 - -### getSimState() -获取 SIM 卡状态。 - -```python -getSimState() -``` - -**返回:** 描述 SIM 状态的字符串 - -### getSubscriberId() -获取订阅者 ID。 - -```python -getSubscriberId() -``` - -**返回:** 字符串订阅者 ID - -## 语音邮件 - -### getVoiceMailAlphaTag() -获取语音邮件字母标签。 - -```python -getVoiceMailAlphaTag() -``` - -**返回:** 字符串语音邮件标签 - -### getVoiceMailNumber() -获取语音邮件号码。 - -```python -getVoiceMailNumber() -``` - -**返回:** 字符串语音邮件号码 - -## 设备信息 - -### getDeviceId() -获取设备 ID(GSM 的 IMEI)。已废弃。 - -```python -getDeviceId() -``` - -**返回:** 字符串设备 ID - -### getDeviceSoftwareVersion() -获取设备软件版本。 - -```python -getDeviceSoftwareVersion() -``` - -**返回:** 字符串软件版本 - -### getLine1Number() -获取线路 1 电话号码。 - -```python -getLine1Number() -``` - -**返回:** 字符串电话号码 - -### checkNetworkRoaming() -检查是否连接到漫游网络。 - -```python -checkNetworkRoaming() -``` - -**返回:** 如果正在漫游则为 True - -## 基站信息 - -### getAllCellInfo() -获取所有基站的信息。 - -```python -getAllCellInfo() -``` - -**返回:** 基站信息列表 - -### setDataEnabled() -启用或禁用移动数据。 - -```python -setDataEnabled(enabled) -``` - -**参数:** -- `enabled` (bool): True 启用,False 禁用 - -## 使用示例 - -```python -import androidhelper - -droid = androidhelper.Android() - -# 获取网络信息 -operator = droid.getNetworkOperatorName().result -print(f"Operator: {operator}") - -network_type = droid.getNetworkType().result -print(f"Network: {network_type}") - -# 获取 SIM 信息 -sim_state = droid.getSimState().result -print(f"SIM: {sim_state}") - -# 获取电话号码 -line1 = droid.getLine1Number().result -print(f"Phone: {line1}") - -# 跟踪电话状态 -droid.startTrackingPhoneState() -print("Tracking phone state...") -droid.stopTrackingPhoneState() -``` diff --git a/source/zh/qsl4a/connectivity/signalstrength.md b/source/zh/qsl4a/connectivity/signalstrength.md deleted file mode 100644 index e134500..0000000 --- a/source/zh/qsl4a/connectivity/signalstrength.md +++ /dev/null @@ -1,71 +0,0 @@ -# 信号强度 API - -监控蜂窝和无线信号强度。 - -## 信号强度方法 - -### startTrackingSignalStrengths() -开始跟踪信号强度变化。生成 'signal_strengths' 事件。 - -```python -startTrackingSignalStrengths() -``` - -### stopTrackingSignalStrengths() -停止跟踪信号强度。 - -```python -stopTrackingSignalStrengths() -``` - -### readSignalStrengths() -读取当前信号强度。 - -```python -readSignalStrengths() -``` - -**返回:** 包含信号强度数据的 Bundle - -### getTelephoneSignalStrengthLevel() -获取电话信号强度等级(0-4)。 - -```python -getTelephoneSignalStrengthLevel() -``` - -**返回:** 整数等级(0=无,1=差,2=一般,3=好,4=优秀) - -### getTelephoneSignalStrengthDetail() -获取详细的电话信号强度信息。 - -```python -getTelephoneSignalStrengthDetail() -``` - -**返回:** 包含详细信号信息的字符串 - -## 使用示例 - -```python -import androidhelper -import time - -droid = androidhelper.Android() - -# 开始跟踪信号强度 -droid.startTrackingSignalStrengths() - -# 等待信号更新 -time.sleep(5) - -# 读取当前信号强度 -signal = droid.readSignalStrengths().result -print(f"Signal: {signal}") - -# 直接获取等级 -level = droid.getTelephoneSignalStrengthLevel().result -print(f"Signal level: {level}/4") - -droid.stopTrackingSignalStrengths() -``` diff --git a/source/zh/qsl4a/connectivity/sms.md b/source/zh/qsl4a/connectivity/sms.md deleted file mode 100644 index 1e628ea..0000000 --- a/source/zh/qsl4a/connectivity/sms.md +++ /dev/null @@ -1,89 +0,0 @@ -# SMS API - -发送和接收 SMS 消息。 - -## 方法 - -### smsSend() -发送 SMS 消息。 - -```python -smsSend(destinationAddress, text) -``` - -**参数:** -- `destinationAddress` (str): 电话号码 -- `text` (str): 消息文本 - -### smsGetMessageCount() -获取消息数量。 - -```python -smsGetMessageCount(unreadOnly=False, folder="inbox") -``` - -### smsGetMessageIds() -获取消息 ID。 - -```python -smsGetMessageIds(unreadOnly=False, folder="inbox") -``` - -### smsGetMessages() -获取消息详情。 - -```python -smsGetMessages(unreadOnly=False, folder="inbox", attributes=None) -``` - -### smsGetMessageById() -通过 ID 获取特定消息。 - -```python -smsGetMessageById(id, attributes=None) -``` - -**参数:** -- `id` (int): 消息 ID -- `attributes` (list, optional): 要检索的特定属性 - -**返回:** 消息数据字典 - -### smsGetAttributes() -获取可用的 SMS 消息属性。 - -```python -smsGetAttributes() -``` - -**返回:** 可用属性名称列表 - -### smsDeleteMessage() -删除消息。 - -```python -smsDeleteMessage(id) -``` - -### smsMarkMessageRead() -将消息标记为已读。 - -```python -smsMarkMessageRead(ids, read=True) -``` - -## 使用示例 - -```python -import androidhelper - -droid = androidhelper.Android() - -# 发送 SMS -droid.smsSend("+1234567890", "Hello from QPython!") - -# 获取未读消息 -messages = droid.smsGetMessages(unreadOnly=True).result -for msg in messages: - print(f"From: {msg['address']}, Text: {msg['body']}") -``` diff --git a/source/zh/qsl4a/connectivity/wifi.md b/source/zh/qsl4a/connectivity/wifi.md deleted file mode 100644 index 5e23d0d..0000000 --- a/source/zh/qsl4a/connectivity/wifi.md +++ /dev/null @@ -1,194 +0,0 @@ -# WiFi API - -控制 WiFi 适配器并获取连接信息。 - -## 适配器控制 - -### checkWifiState() -检查 WiFi 是否已启用。 - -```python -checkWifiState() -``` - -**返回:** 如果 WiFi 已启用则为 True,否则为 False - -### toggleWifiState() -打开或关闭 WiFi。 - -```python -toggleWifiState(enabled=None) -``` - -**参数:** -- `enabled` (bool): True 启用,False 禁用,None 切换 - -**返回:** 如果操作成功则为 True - -### wifiStartScan() -开始扫描可用的 WiFi 网络。 - -```python -wifiStartScan() -``` - -### wifiGetScanResults() -获取发现的 WiFi 网络列表。 - -```python -wifiGetScanResults() -``` - -**返回:** 接入点信息列表 - -## 连接管理 - -### wifiGetConnectionInfo() -获取详细连接信息。 - -```python -wifiGetConnectionInfo() -``` - -**返回:** 包含 SSID、BSSID、IP 地址等连接详情的字典 - -### getConnectedInfo() -获取连接的 WiFi 网络信息(简化版)。 - -```python -getConnectedInfo() -``` - -**返回:** 包含 SSID、BSSID、信号强度的字典 - -### getDhcpInfo() -获取当前连接的 DHCP 信息。 - -```python -getDhcpInfo(ipConvertToString=True) -``` - -**参数:** -- `ipConvertToString` (bool): 将 IP 地址转换为字符串格式(默认:True) - -**返回:** 包含 IP、网关、DNS 等 DHCP 信息的字典 - -### wifiDisconnect() -断开当前 WiFi 网络连接。 - -```python -wifiDisconnect() -``` - -### wifiReconnect() -重新连接到当前网络。 - -```python -wifiReconnect() -``` - -### wifiReassociate() -重新关联当前接入点。 - -```python -wifiReassociate() -``` - -## 热点 - -### wifiGetApState() -获取 WiFi AP(热点)状态。 - -```python -wifiGetApState() -``` - -**返回:** 热点状态 - -## WiFi 锁 - -### wifiLockAcquireFull() -获取完整 WiFi 锁(即使屏幕关闭也保持 WiFi 活动)。 - -```python -wifiLockAcquireFull() -``` - -### wifiLockAcquireScanOnly() -获取仅扫描 WiFi 锁。 - -```python -wifiLockAcquireScanOnly() -``` - -### wifiLockRelease() -释放 WiFi 锁。 - -```python -wifiLockRelease() -``` - -## 使用示例 - -```python -import androidhelper -import time - -droid = androidhelper.Android() - -# 检查 WiFi 状态 -if droid.checkWifiState().result: - print("WiFi is enabled") -else: - print("Enabling WiFi...") - droid.toggleWifiState(True) - time.sleep(2) - -# 开始扫描 -droid.wifiStartScan() -time.sleep(3) - -# 获取扫描结果 -networks = droid.wifiGetScanResults().result -for network in networks: - print(f"SSID: {network.get('SSID')}, Signal: {network.get('level')} dBm") - -# 获取连接信息 -info = droid.wifiGetConnectionInfo().result -if info: - print(f"Connected to: {info.get('ssid')}") - print(f"BSSID: {info.get('bssid')}") - print(f"IP: {info.get('ip_address')}") - -# 获取 DHCP 信息 -dhcp = droid.getDhcpInfo().result -if dhcp: - print(f"Gateway: {dhcp.get('gateway')}") - print(f"DNS: {dhcp.get('dns1')}") - -# 获取简化的连接信息 -connected = droid.getConnectedInfo().result -print(f"SSID: {connected.get('ssid')}, Signal: {connected.get('level')} dBm") - -# 断开并重新连接 -droid.wifiDisconnect() -time.sleep(1) -droid.wifiReconnect() - -# 重新关联接入点 -droid.wifiReassociate() - -# 检查热点状态 -ap_state = droid.wifiGetApState().result -print(f"Hotspot state: {ap_state}") - -# 获取 WiFi 锁以进行后台操作 -droid.wifiLockAcquireFull() -# ... 执行工作 ... -droid.wifiLockRelease() - -# 或使用仅扫描锁以进行较轻的后台操作 -droid.wifiLockAcquireScanOnly() -# ... 执行扫描工作 ... -droid.wifiLockRelease() -``` diff --git a/source/zh/qsl4a/core/android-base.md b/source/zh/qsl4a/core/android-base.md deleted file mode 100644 index b7f5b19..0000000 --- a/source/zh/qsl4a/core/android-base.md +++ /dev/null @@ -1,129 +0,0 @@ -# Android 基础类 - -`Android` 类是 QSL4A 的核心,提供了与 Android 运行时的连接和 RPC 机制。 - -## 模块导入 - -```python -# 功能完整版本(推荐) -import androidhelper -droid = androidhelper.Android() - -# 简化版本(快速,单实例) -import android -droid = android.droid -``` - -## 类:Android - -### 构造函数 - -```python -Android(addr=None) -``` - -**参数:** -- `addr` (tuple, optional): (HOST, PORT) 地址。如果为 None,则使用环境变量 `AP_HOST` 和 `AP_PORT` - -**环境变量:** -- `AP_HOST` - 服务器主机地址 -- `AP_PORT` - 服务器端口 -- `AP_HANDSHAKE` - 认证令牌 - -### 核心方法 - -#### _rpc() -调用 Android 函数的内部 RPC 方法。 - -```python -_rpc(method, *args) -``` - -**参数:** -- `method` (str): 要调用的方法名 -- `*args`: 方法的可变参数 - -**返回:** `Result` 命名元组,包含以下字段: -- `id` (int): 请求 ID -- `result`: 方法返回值 -- `error` (str or None): 错误信息(如果失败) - -#### __getattr__() -动态方法分发器。所有 Android 方法都通过属性查找访问。 - -```python -# 以下两种方式等价: -droid.makeToast("Hello") -droid._rpc("makeToast", "Hello") -``` - -## 独立函数(android.py) - -使用简化的 `android` 模块时: - -### jsla() -发送 JSON-RPC 请求并返回原始响应。 - -```python -jsla(method, *params) -``` - -**返回:** JSON 字符串响应 - -### rsla() -发送请求并仅返回结果。 - -```python -rsla(method, *params) -``` - -**返回:** RPC 调用的结果值 - -### esla() -发送请求,错误时抛出异常。 - -```python -esla(method, *params) -``` - -**抛出:** 如果 error 字段不为 None 则抛出异常 - -### nsla() -发送请求并返回 Result 命名元组。 - -```python -nsla(method, *params) -``` - -**返回:** Result 命名元组 - -## 使用示例 - -### 基本连接 - -```python -import androidhelper - -# 连接到 Android 运行时 -droid = androidhelper.Android() - -# 通过显示 toast 来检查连接 -droid.makeToast("Connected!") -``` - -### 错误处理 - -```python -result = droid.someMethod() -if result.error: - print(f"Error: {result.error}") -else: - print(f"Success: {result.result}") -``` - -### 直接 RPC 调用 - -```python -# 直接调用任意方法 -result = droid._rpc("makeToast", "Hello World") -``` diff --git a/source/zh/qsl4a/core/events.md b/source/zh/qsl4a/core/events.md deleted file mode 100644 index ad9abdf..0000000 --- a/source/zh/qsl4a/core/events.md +++ /dev/null @@ -1,245 +0,0 @@ -# 事件系统 - -QSL4A 提供了一个事件系统,用于处理来自 Android 的异步事件,如传感器更新、位置变化和自定义广播。 - -## 事件基础 - -事件存储在缓冲区中,可以轮询或等待。每个事件包含: -- `name`: 事件类型/名称 -- `data`: 事件负载数据 -- `time`: 时间戳 - -## 事件方法 - -### eventClearBuffer() -清除缓冲区中的所有待处理事件。 - -```python -eventClearBuffer() -``` - -**返回:** None - -### eventPoll() -轮询缓冲区中的事件。 - -```python -eventPoll(number_of_events=1) -``` - -**参数:** -- `number_of_events` (int): 要检索的最大事件数(默认:1) - -**返回:** 事件对象列表 - -### eventWait() -等待任意事件。 - -```python -eventWait(timeout=None) -``` - -**参数:** -- `timeout` (int, optional): 超时时间(秒)。None = 永久等待 - -**返回:** 事件对象,如果超时则返回 None - -### eventWaitFor() -等待特定事件。 - -```python -eventWaitFor(eventName, timeout=None) -``` - -**参数:** -- `eventName` (str): 要等待的事件名称 -- `timeout` (int, optional): 超时时间(秒) - -**返回:** 事件对象,如果超时则返回 None - -### eventPost() -发布自定义事件。 - -```python -eventPost(name, data, enqueue=None) -``` - -**参数:** -- `name` (str): 事件名称 -- `data`: 事件数据(任意类型) -- `enqueue` (bool, optional): 如果为 True 则添加到队列 - -### receiveEvent() -接收事件(阻塞)。 - -```python -receiveEvent() -``` - -**返回:** 事件对象 - -## 广播事件 - -注册系统广播事件。 - -### eventRegisterForBroadcast() -注册接收广播事件。 - -```python -eventRegisterForBroadcast(category, enqueue=True) -``` - -**参数:** -- `category` (str): 广播类别/动作 -- `enqueue` (bool): 添加到事件队列 - -### eventUnregisterForBroadcast() -取消注册广播事件。 - -```python -eventUnregisterForBroadcast(category) -``` - -### eventGetBrodcastCategories() -获取已注册的广播类别。 - -```python -eventGetBrodcastCategories() -``` - -**返回:** 已注册类别列表 - -## 事件分发器) - -### startEventDispatcher() -打开一个可以读取已发布事件的套接字。) - -```python -startEventDispatcher(port=0) -``` - -**参数:** -- `port` (int, optional): 监听的端口(默认:0 = 自动选择) - -**返回:** 正在监听的端口号 - -### stopEventDispatcher() -停止事件服务器。) - -```python -stopEventDispatcher() -``` - -## 已废弃方法 - -### rpcPostEvent() -将事件发布到事件队列。(已废弃,请使用 eventPost) - -```python -rpcPostEvent(name, data) -``` - -**参数:** -- `name` (str): 事件名称 -- `data`: 事件数据 - -## 使用示例 - -### 基本事件轮询 - -```python -import androidhelper - -droid = androidhelper.Android() - -# 清除旧事件 -droid.eventClearBuffer() - -# 轮询事件 -events = droid.eventPoll(5).result -for event in events: - print(f"Event: {event['name']}, Data: {event['data']}") -``` - -### 等待事件 - -```python -# 等待任意事件,带超时 -event = droid.eventWait(timeout=10).result -if event: - print(f"Got event: {event['name']}") -``` - -### 等待特定事件 - -```python -# 等待传感器事件 -event = droid.eventWaitFor('screen', timeout=5).result -if event: - print(f"Screen event: {event['data']}") -``` - -### 发布自定义事件 - -```python -# 发布自定义事件 -droid.eventPost('my_event', {'key': 'value'}) - -# 等待它 -event = droid.eventWaitFor('my_event', timeout=1).result -``` - -### 广播接收器 - -```python -# 注册屏幕开关事件 -droid.eventRegisterForBroadcast('android.intent.action.SCREEN_ON') -droid.eventRegisterForBroadcast('android.intent.action.SCREEN_OFF') - -# 等待屏幕事件 -while True: - event = droid.receiveEvent().result - if event['name'] == 'android.intent.action.SCREEN_ON': - print("Screen turned on") - elif event['name'] == 'android.intent.action.SCREEN_OFF': - print("Screen turned off") -``` - -### 传感器事件处理 - -```python -# 开始感知 -droid.startSensingTimed(1, 250) - -# 处理传感器事件 -for _ in range(100): - event = droid.eventWait(timeout=1).result - if event and event['name'] == 'sensors': - data = event['data'] - print(f"Accel: {data['xforce']}, {data['yforce']}, {data['zforce']}") - -droid.stopSensing() -``` - -## 常见事件类型 - -| 事件名称 | 描述 | 来源 | -|------------|-------------|--------| -| `sensors` | 传感器数据更新 | startSensing* | -| `location` | GPS 位置更新 | startLocating | -| `phone` | 电话状态变化 | startTrackingPhoneState | -| `signal` | 信号强度变化 | startTrackingSignalStrength | -| `screen` | 截图就绪 | fullGetScreenShot | -| `dialog` | 对话框响应 | dialogShow* | - -## 事件数据结构 - -```python -{ - 'name': 'event_name', - 'data': { - # 事件特定数据 - }, - 'time': 1234567890 # 时间戳 -} -``` diff --git a/source/zh/qsl4a/core/intent.md b/source/zh/qsl4a/core/intent.md deleted file mode 100644 index 1493341..0000000 --- a/source/zh/qsl4a/core/intent.md +++ /dev/null @@ -1,334 +0,0 @@ -# Intent 系统 - -Android Intent 用于启动活动、发送广播和应用间通信。QSL4A 通过 `Intent` 模块提供完整的 Intent 支持。 - -## 模块导入 - -```python -import androidhelper -don = androidhelper.Android() -``` - -## Intent 常量 - -通过 `droid.Intent` 访问: - -### 操作 - -| 常量 | 值 | 用途 | -|----------|-------|-------| -| `ACTION_MAIN` | android.intent.action.MAIN | 应用入口点 | -| `ACTION_VIEW` | android.intent.action.VIEW | 查看内容 | -| `ACTION_EDIT` | android.intent.action.EDIT | 编辑内容 | -| `ACTION_PICK` | android.intent.action.PICK | 选择项目 | -| `ACTION_SEND` | android.intent.action.SEND | 分享内容 | -| `ACTION_SEARCH` | android.intent.action.SEARCH | 搜索 | - -### 标志 - -| 常量 | 值 | 用途 | -|----------|-------|-------| -| `FLAG_ACTIVITY_NEW_TASK` | 268435456 | 启动新任务 | -| `FLAG_ACTIVITY_CLEAR_TASK` | 32768 | 清除任务堆栈 | -| `FLAG_ACTIVITY_NEW_DOCUMENT` | 524288 | 新文档模式 | - -### 附加数据 - -| 常量 | 用途 | -|----------|-------| -| `EXTRA_TEXT` | 文本内容 | -| `EXTRA_STREAM` | 文件 URI | -| `EXTRA_SUBJECT` | 主题行 | -| `EXTRA_EMAIL` | 电子邮件地址 | - -## 核心方法 - -### makeIntent() -创建 Intent 对象。 - -```python -makeIntent(action, uri=None, type=None, extras=None, categories=None, - packagename=None, classname=None, flags=None) -``` - -**参数:** -- `action` (str): Intent 操作(例如 `droid.Intent.ACTION_VIEW`) -- `uri` (str, optional): 数据 URI -- `type` (str, optional): MIME 类型 -- `extras` (dict, optional): 附加数据 -- `categories` (list, optional): Intent 类别 -- `packagename` (str, optional): 目标包 -- `classname` (str, optional): 目标类 -- `flags` (int, optional): Intent 标志 - -**返回:** Intent 对象 - -### startActivityIntent() -使用 Intent 启动 Activity。 - -```python -startActivityIntent(intent, wait=None) -``` - -**参数:** -- `intent`: makeIntent() 返回的 Intent 对象 -- `wait` (bool, optional): 阻塞直到活动关闭 - -### startActivityForResultIntent() -启动活动并等待结果。 - -```python -startActivityForResultIntent(intent) -``` - -**返回:** Activity 结果 - -### sendBroadcastIntent() -发送广播。 - -```python -sendBroadcastIntent(intent) -``` - -### view() -通过 URI 查看内容。 - -```python -view(uri, type=None, extras=None) -``` - -### pick() -从 URI 选择内容。 - -```python -pick(uri) -``` - -## 常见 Intent 方法) - -### scanBarcode() -启动条码扫描器。 - -```python -scanBarcode() -``` - -**返回:** 扫描的条码字符串 - -### send() -通过分享 Intent 发送内容。 - -```python -send(type, content) -``` - -**参数:** -- `type` (str): MIME 类型 -- `content` (str): 要分享的内容 - -### sendText() -发送文本内容。 - -```python -sendText(text) -``` - -**参数:** -- `text` (str): 要发送的文本 - -### sendEmail() -发送电子邮件。 - -```python -sendEmail(to, subject, body, attachment=None) -``` - -**参数:** -- `to` (str or list): 收件人电子邮件地址 -- `subject` (str): 电子邮件主题 -- `body` (str): 电子邮件正文 -- `attachment` (str, optional): 附件文件路径 - -### pathToUri() -将文件路径转换为内容 URI。 - -```python -pathToUri(path) -``` - -**参数:** -- `path` (str): 文件路径 - -**返回:** 内容 URI 字符串 - -### openFile() -用适当的应用程序打开文件。 - -```python -openFile(path) -``` - -**参数:** -- `path` (str): 要打开的文件路径 - -### sendFile() -通过分享 Intent 发送文件。 - -```python -sendFile(path) -``` - -**参数:** -- `path` (str): 要发送的文件路径 - -### getPathType() -获取文件路径的 MIME 类型。 - -```python -getPathType(path) -``` - -**参数:** -- `path` (str): 文件路径 - -**返回:** MIME 类型字符串 - -### viewMap() -在指定位置打开地图。 - -```python -viewMap(latitude, longitude) -``` - -**参数:** -- `latitude` (float): 纬度 -- `longitude` (float): 经度 - -### viewContacts() -打开联系人应用。 - -```python -viewContacts() -``` - -### search() -执行网络搜索。 - -```python -search(query) -``` - -**参数:** -- `query` (str): 搜索查询 - -### viewHtml() -查看 HTML 内容。 - -```python -viewHtml(content, encoding=None) -``` - -**参数:** -- `content` (str): HTML 内容 -- `encoding` (str, optional): 字符编码 - -### webViewShow() -在 WebView 中显示网页内容。已废弃,请使用 viewHtml。 - -```python -webViewShow(url) -``` - -**参数:** -- `url` (str): 网页 URL - -### editorOpen() -打开文本编辑器。 - -```python -editorOpen(path=None, create=False) -``` - -**参数:** -- `path` (str, optional): 要编辑的文件路径 -- `create` (bool, optional): 如果不存在则创建 - -## 辅助类:Uri - -创建 Intent 的 URI 对象: - -```python -from androidhelper.Intent import Uri - -uri = Uri("file:///sdcard/test.txt") -``` - -## 使用示例 - -### 打开网页 - -```python -intent = droid.makeIntent( - action=droid.Intent.ACTION_VIEW, - uri="http://www.example.com" -).result -droid.startActivityIntent(intent) -``` - -### 分享文本 - -```python -intent = droid.makeIntent( - action=droid.Intent.ACTION_SEND, - extras={ - droid.Intent.EXTRA_TEXT: "Hello from QPython!", - droid.Intent.EXTRA_SUBJECT: "Test" - }, - type="text/plain" -).result -droid.startActivityIntent(intent) -``` - -### 打开文件 - -```python -intent = droid.makeIntent( - action=droid.Intent.ACTION_VIEW, - uri="file:///sdcard/document.pdf", - type="application/pdf" -).result -droid.startActivityIntent(intent) -``` - -### 选择联系人 - -```python -result = droid.pickContact() -contact_uri = result.result -``` - -### 发送电子邮件 - -```python -intent = droid.makeIntent( - action=droid.Intent.ACTION_SEND, - extras={ - droid.Intent.EXTRA_EMAIL: ["test@example.com"], - droid.Intent.EXTRA_SUBJECT: "Hello", - droid.Intent.EXTRA_TEXT: "Message body" - }, - type="message/rfc822" -).result -droid.startActivityIntent(intent) -``` - -### 打开应用 - -```python -intent = droid.makeIntent( - action=droid.Intent.ACTION_MAIN, - packagename="com.android.settings", - classname="com.android.settings.Settings" -).result -droid.startActivityIntent(intent) -``` diff --git a/source/zh/qsl4a/hardware/bluetooth.md b/source/zh/qsl4a/hardware/bluetooth.md deleted file mode 100644 index e99e745..0000000 --- a/source/zh/qsl4a/hardware/bluetooth.md +++ /dev/null @@ -1,201 +0,0 @@ -# 蓝牙 API - -控制蓝牙适配器并与蓝牙设备通信。 - -## 适配器控制 - -### toggleBluetoothState() -打开/关闭蓝牙。 - -```python -toggleBluetoothState(enabled=None, prompt=True) -``` - -**参数:** -- `enabled` (bool): True=开,False=关,None=切换 -- `prompt` (bool): 显示用户提示 - -### checkBluetoothState() -检查蓝牙是否已启用。 - -```python -checkBluetoothState() -``` - -**返回:** True/False - -### GetLocalName() -获取蓝牙设备名称。 - -```python -GetLocalName() -``` - -### SetLocalName() -设置蓝牙设备名称。 - -```python -SetLocalName(name) -``` - -### GetScanMode() -获取可发现性模式。 - -```python -GetScanMode() -``` - -**返回:** -- -1: 已禁用 -- 0: 不可发现,不可连接 -- 1: 可连接,不可发现 -- 3: 可连接且可发现 - -### MakeDiscoverable() -使设备可发现。 - -```python -MakeDiscoverable(duration=300) -``` - -**参数:** -- `duration` (int): 可发现的秒数 - -## 发现 - -### DiscoveryStart() -开始设备发现。 - -```python -DiscoveryStart() -``` - -### DiscoveryCancel() -取消发现。 - -```python -DiscoveryCancel() -``` - -### GetReceivedDevices() -获取发现的设备。 - -```python -GetReceivedDevices() -``` - -**返回:** 设备信息字典列表 - -### GetBondedDevices() -获取配对设备。 - -```python -GetBondedDevices() -``` - -**返回:** 配对设备信息列表 - -## 连接 - -### Connect() -连接到设备。 - -```python -Connect(uuid="457807c0-4897-11df-9879-0800200c9a66", address=None) -``` - -**参数:** -- `uuid` (str): 服务 UUID -- `address` (str): 设备地址(None = 显示选择器) - -**返回:** 如果成功则为 True - -### Accept() -接受传入连接。 - -```python -Accept(uuid="457807c0-4897-11df-9879-0800200c9a66", timeout=0) -``` - -### ActiveConnections() -检查活动连接。 - -```python -ActiveConnections() -``` - -### Stop() -断开连接。 - -```python -Stop(connID=None) -``` - -## 通信 - -### Write() -发送 ASCII 数据。 - -```python -Write(ascii, connID="") -``` - -### WriteBinary() -发送二进制数据(base64 编码)。 - -```python -WriteBinary(base64, connID=None) -``` - -### Read() -读取 ASCII 数据。 - -```python -Read(bufferSize=4096, connID=None) -``` - -### ReadBinary() -读取二进制数据。 - -```python -ReadBinary(bufferSize=4096, connID=None) -``` - -### ReadLine() -读取一行。 - -```python -ReadLine(connID=None) -``` - -### ReadReady() -检查是否有可用数据。 - -```python -ReadReady(connID=None) -``` - -## 使用示例 - -```python -import androidhelper - -droid = androidhelper.Android() - -# 启用蓝牙 -droid.toggleBluetoothState(True) - -# 获取配对设备 -devices = droid.GetBondedDevices().result -for dev in devices: - print(f"{dev['name']}: {dev['address']}") - -# 连接到设备 -droid.Connect(address="00:11:22:33:44:55") - -# 发送数据 -droid.Write("Hello Bluetooth!") - -# 读取响应 -data = droid.Read(1024).result -``` diff --git a/source/zh/qsl4a/hardware/camera.md b/source/zh/qsl4a/hardware/camera.md deleted file mode 100644 index 404576b..0000000 --- a/source/zh/qsl4a/hardware/camera.md +++ /dev/null @@ -1,182 +0,0 @@ -# 相机 API - -拍摄照片和录制视频。 - -## 照片拍摄 - -### takePicture() -使用默认相机拍摄照片。 - -```python -takePicture(path=None) -``` - -**参数:** -- `path` (str, optional): 保存路径。如果为 None,则返回图像数据 - -**返回:** 图像路径或图像数据 - -### cameraCapturePicture() -使用高级相机控制拍摄照片。 - -```python -cameraCapturePicture(targetPath=None, cameraId=0, useAutoFocus=True) -``` - -**参数:** -- `targetPath` (str, optional): 保存路径 -- `cameraId` (int): 使用的相机(0 = 后置,1 = 前置) -- `useAutoFocus` (bool): 启用自动对焦 - -**返回:** 拍摄的图像路径 - -### cameraSetTorchMode() -控制相机闪光灯/手电筒。 - -```python -cameraSetTorchMode(enabled) -``` - -**参数:** -- `enabled` (bool): True 打开,False 关闭 - -**返回:** 如果成功则为 True - -### imageReaderGetScreenShot() -截图。 - -```python -imageReaderGetScreenShot(path=None, delayMilliSec=1000) -``` - -**参数:** -- `path` (str, optional): 保存路径 -- `delayMilliSec` (int): 拍摄前延迟 - -## 视频录制 - -### takeVideo() -使用默认设置录制视频。 - -```python -takeVideo(path=None, quality=1) -``` - -**参数:** -- `path` (str, optional): 保存路径 -- `quality` (int): 视频质量(0-4) - - 0: 160x120 - - 1: 320x240 - - 2: 352x288 - - 3: 640x480 - - 4: 800x480 - -### recorderCaptureVideo() -使用高级控制录制视频。 - -```python -recorderCaptureVideo(targetPath=None, duration=10, cameraId=0, quality=8) -``` - -**参数:** -- `targetPath` (str, optional): 保存路径 -- `duration` (int): 录制时长(秒)(默认:10) -- `cameraId` (int): 使用的相机(0 = 后置,1 = 前置) -- `quality` (int): 视频质量(0-8,越高越好) - -**返回:** 视频文件路径 - -### recordAudio() -录制音频。 - -```python -recordAudio() -``` - -**返回:** 音频文件路径 - -## 屏幕录制 - -### recorderStartScreenRecord() -开始屏幕录制。 - -```python -recorderStartScreenRecord(path=None, audio=1, targetPixels=None, - frameRate=30, bitRate=None, rotation=False, - autoStart=True) -``` - -**参数:** -- `path` (str): 保存路径 -- `audio` (int): 音频源(0=无,1=麦克风,2=内部) -- `targetPixels` (int): 分辨率 -- `frameRate` (int): FPS -- `bitRate` (int): 比特率 -- `rotation` (bool): 旋转输出 -- `autoStart` (bool): 立即开始 - -### recorderStart() -开始录制。 - -```python -recorderStart() -``` - -### recorderPause() -暂停录制。 - -```python -recorderPause() -``` - -### recorderResume() -恢复录制。 - -```python -recorderResume() -``` - -## 音频音量检测 - -### recorderSoundVolumeDetect() -开始音量检测。 - -```python -recorderSoundVolumeDetect(interval=100) -``` - -### recorderSoundVolumeGetDb() -获取当前音量(分贝)。 - -```python -recorderSoundVolumeGetDb() -``` - -## 使用示例 - -```python -import androidhelper - -droid = androidhelper.Android() - -# 使用默认相机拍照 -photo_path = droid.takePicture("/sdcard/photo.jpg").result -print(f"Photo saved: {photo_path}") - -# 使用前置相机和自动对焦拍照 -camera_path = droid.cameraCapturePicture("/sdcard/selfie.jpg", cameraId=1, useAutoFocus=True).result -print(f"Front camera photo: {camera_path}") - -# 控制闪光灯 -droid.cameraSetTorchMode(True) # 打开闪光灯 - -# 使用默认设置录制视频 -video_path = droid.takeVideo("/sdcard/video.mp4", quality=3).result -print(f"Video saved: {video_path}") - -# 使用高级控制录制视频 -video_path = droid.recorderCaptureVideo("/sdcard/movie.mp4", duration=30, cameraId=0, quality=8).result - -# 截图 -ss_path = droid.imageReaderGetScreenShot("/sdcard/screenshot.png", 500).result -``` diff --git a/source/zh/qsl4a/hardware/recorder.md b/source/zh/qsl4a/hardware/recorder.md deleted file mode 100644 index 942eca4..0000000 --- a/source/zh/qsl4a/hardware/recorder.md +++ /dev/null @@ -1,124 +0,0 @@ -# 音频录制 API - -从麦克风和设备屏幕录制音频。 - -## 音频录制 - -### recordAudio() -从麦克风录制音频。 - -```python -recordAudio() -``` - -**返回:** 录制的音频文件路径 - -### recorderStartMicrophone() -开始从麦克风录制到特定文件。 - -```python -recorderStartMicrophone(targetPath=None) -``` - -**参数:** -- `targetPath` (str, optional): 保存录制的路径 - -## 屏幕录制 - -### recorderStartScreenRecord() -开始带音频的屏幕录制。 - -```python -recorderStartScreenRecord(path=None, audio=1, targetPixels=None, - frameRate=30, bitRate=None, rotation=False, - autoStart=True) -``` - -**参数:** -- `path` (str): 视频文件保存路径 -- `audio` (int): 音频源(0=无,1=麦克风,2=内部音频) -- `targetPixels` (int): 目标分辨率(像素) -- `frameRate` (int): 每秒帧数(默认:30) -- `bitRate` (int): 视频比特率 -- `rotation` (bool): 旋转输出视频 -- `autoStart` (bool): 立即开始录制 - -**返回:** 操作结果 - -### recorderStart() -开始屏幕录制(当 autoStart=False 时)。 - -```python -recorderStart() -``` - -### recorderPause() -暂停正在进行的屏幕录制。 - -```python -recorderPause() -``` - -### recorderResume() -恢复暂停的屏幕录制。 - -```python -recorderResume() -``` - -## 音频音量检测 - -### recorderSoundVolumeDetect() -开始监控音量级别。 - -```python -recorderSoundVolumeDetect(interval=100) -``` - -**参数:** -- `interval` (int): 检测间隔(毫秒)(默认:100) - -### recorderSoundVolumeGetDb() -获取当前音量(分贝)。 - -```python -recorderSoundVolumeGetDb() -``` - -**返回:** 当前音量级别(dB) - -## 使用示例 - -```python -import androidhelper -import time - -droid = androidhelper.Android() - -# 从麦克风录制音频 -print("Recording audio...") -audio_path = droid.recordAudio().result -print(f"Saved to: {audio_path}") - -# 录制到特定文件 -droid.recorderStartMicrophone("/sdcard/my_recording.mp3") -time.sleep(5) -droid.recorderStop() - -# 带音频录制屏幕 -print("Starting screen recording...") -droid.recorderStartScreenRecord( - path="/sdcard/screen_record.mp4", - audio=1, - frameRate=30, - autoStart=True -) -time.sleep(10) -droid.recorderStop() - -# 监控音量 -droid.recorderSoundVolumeDetect(interval=100) -time.sleep(3) -volume = droid.recorderSoundVolumeGetDb().result -print(f"Current volume: {volume} dB") -``` diff --git a/source/zh/qsl4a/hardware/usbserial.md b/source/zh/qsl4a/hardware/usbserial.md deleted file mode 100644 index 6557f08..0000000 --- a/source/zh/qsl4a/hardware/usbserial.md +++ /dev/null @@ -1,161 +0,0 @@ -# USB 主机串行 API - -与 USB 串行设备通信(需要 USB OTG 支持和 Android 3.1+)。 - -## USB 串行方法) - -### usbHostSerialOpen() -打开与 USB 串行设备的连接。 - -```python -usbHostSerialOpen(device, baudRate=9600) -``` - -**参数:** -- `device` (str): USB 设备路径或标识符 -- `baudRate` (int): 波特率(默认:9600) - -**返回:** 如果打开成功则为 True - -### usbHostSerialClose() -关闭 USB 串行连接。 - -```python -usbHostSerialClose() -``` - -### usbHostSerialRead() -从 USB 串行读取数据。 - -```python -usbHostSerialRead(bufferSize=1024) -``` - -**参数:** -- `bufferSize` (int): 要读取的最大字节数(默认:1024) - -**返回:** 读取的数据字符串 - -### usbHostSerialWrite() -向 USB 串行写入数据。 - -```python -usbHostSerialWrite(data) -``` - -**参数:** -- `data` (str): 要写入的字符串数据 - -### usbHostSerialAvailable() -检查是否有可读取的数据。 - -```python -usbHostSerialAvailable() -``` - -**返回:** 可用字节数 - -## 配置方法) - -### usbHostSerialSetBaudRate() -设置波特率。 - -```python -usbHostSerialSetBaudRate(baudRate) -``` - -**参数:** -- `baudRate` (int): 波特率 - -### usbHostSerialSetDataBits() -设置数据位(5、6、7 或 8)。 - -```python -usbHostSerialSetDataBits(dataBits) -``` - -**参数:** -- `dataBits` (int): 数据位(5-8) - -### usbHostSerialSetStopBits() -设置停止位(1、1.5 或 2)。 - -```python -usbHostSerialSetStopBits(stopBits) -``` - -**参数:** -- `stopBits` (float): 停止位(1、1.5 或 2) - -### usbHostSerialSetParity() -设置校验位(无、奇、偶、标记、空格)。 - -```python -usbHostSerialSetParity(parity) -``` - -**参数:** -- `parity` (str): 校验模式('none'、'odd'、'even'、'mark'、'space') - -### usbHostSerialSetFlowControl() -设置流控制(无、硬件、软件)。 - -```python -usbHostSerialSetFlowControl(flowControl) -``` - -**参数:** -- `flowControl` (str): 流控制模式('none'、'hardware'、'software') - -### usbHostSerialReadHex() -以十六进制字符串读取数据。 - -```python -usbHostSerialReadHex(bufferSize=1024) -``` - -**参数:** -- `bufferSize` (int): 要读取的最大字节数 - -**返回:** 十六进制字符串 - -### usbHostSerialWriteHex() -从十六进制字符串写入数据。 - -```python -usbHostSerialWriteHex(hexString) -``` - -**参数:** -- `hexString` (str): 要写入的十六进制字符串 - -## 使用示例 - -```python -import androidhelper - -droid = androidhelper.Android() - -# 打开 USB 串行连接 -if droid.usbHostSerialOpen("/dev/bus/usb/001/001", 115200).result: - print("USB serial opened") - - # 写入数据 - droid.usbHostSerialWrite("AT\r") - - # 读取响应 - response = droid.usbHostSerialRead(1024).result - print(f"Response: {response}") - - # 或使用十六进制 - droid.usbHostSerialWriteHex("41540D0A") # "AT\r\n" - - # 关闭连接 - droid.usbHostSerialClose() -``` - -**注意:** USB 串行需要: -- Android 3.1+ (API 12) -- USB OTG 线/适配器 -- 设备上的 USB 主机模式支持 -- 兼容的串行设备 diff --git a/source/zh/qsl4a/hardware/webcam.md b/source/zh/qsl4a/hardware/webcam.md deleted file mode 100644 index c0d06c6..0000000 --- a/source/zh/qsl4a/hardware/webcam.md +++ /dev/null @@ -1,98 +0,0 @@ -# 网络摄像头 API - -使用 MJPEG 从设备相机流式传输视频。 - -## MJPEG 流方法 - -### webcamStart() -从网络摄像头启动 MJPEG 流。 - -```python -webcamStart(resolutionLevel=0, jpegQuality=20, port=0) -``` - -**参数:** -- `resolutionLevel` (int): 分辨率级别(默认:0) -- `jpegQuality` (int): JPEG 质量 1-100(默认:20) -- `port` (int): 端口号(默认:0 = 自动) - -**返回:** 流的 (地址, 端口) 元组 - -### webcamAdjustQuality() -调整活动网络摄像头流的质量。 - -```python -webcamAdjustQuality(resolutionLevel=0, jpegQuality=20) -``` - -**参数:** -- `resolutionLevel` (int): 分辨率级别 -- `jpegQuality` (int): JPEG 质量 1-100 - -### webcamStop() -停止网络摄像头流。 - -```python -webcamStop() -``` - -## 相机预览方法 - -### cameraStartPreview() -开始带事件生成的相机预览模式。 - -```python -cameraStartPreview(resolutionLevel=0, jpegQuality=20, filepath=None) -``` - -**参数:** -- `resolutionLevel` (int): 分辨率级别(默认:0) -- `jpegQuality` (int): JPEG 质量(默认:20) -- `filepath` (str, optional): 保存预览帧的文件路径 - -**返回:** 如果成功则为 True - -**注意:** 生成带有帧数据的 'preview' 事件。 - -### cameraStopPreview() -停止相机预览。 - -```python -cameraStopPreview() -``` - -## 使用示例 - -```python -import androidhelper -import time - -droid = androidhelper.Android() - -# 启动网络摄像头流 -stream_info = droid.webcamStart( - resolutionLevel=0, - jpegQuality=30, - port=8080 -).result -print(f"Stream available at {stream_info[0]}:{stream_info[1]}") - -# 流式传输时调整质量 -time.sleep(5) -droid.webcamAdjustQuality(resolutionLevel=1, jpegQuality=50) - -# 完成后停止 -droid.webcamStop() - -# 或使用预览模式 -print("Starting preview...") -droid.cameraStartPreview() - -# 等待预览事件 -for i in range(10): - event = droid.eventWait(timeout=1).result - if event and event['name'] == 'preview': - print(f"Got preview frame: {event['data']}") - -droid.cameraStopPreview() -``` diff --git a/source/zh/qsl4a/index.md b/source/zh/qsl4a/index.md deleted file mode 100644 index 7561aee..0000000 --- a/source/zh/qsl4a/index.md +++ /dev/null @@ -1,91 +0,0 @@ -# QSL4A(Android 脚本层)API 文档 - -QSL4A 是 QPython 的 Android 脚本层,允许您使用 Python 控制 Android 设备功能。 - -## 快速开始 - -```python -import androidhelper - -droid = androidhelper.Android() - -# 显示 toast 消息 -droid.makeToast('Hello QPython!') - -# 震动设备 -droid.vibrate(500) - -# 获取电池电量(先开始监控) -import time -droid.batteryStartMonitoring() -time.sleep(0.5) # 等待数据 -battery = droid.readBatteryData().result -print(f"电池:{battery['level']}%") -``` - -## 文档结构 - -### [核心模块](core/) -- [Android 基础](core/android-base.md) - 核心连接和 RPC -- [Intent 系统](core/intent.md) - Android Intent 操作 -- [事件系统](core/events.md) - 事件处理和广播 - -### [UI 组件](ui/) -- [对话框](ui/dialogs.md) - 警报、输入、选择对话框 -- [全屏 UI](ui/fullscreen.md) - 自定义布局 UI -- [悬浮窗](ui/floatview.md) - 悬浮窗口 -- [辅助功能](ui/accessibility.md) - 屏幕自动化 - -### [系统](system/) -- [电池](system/battery.md) - 电池监控 -- [传感器](system/sensors.md) - 设备传感器 -- [应用](system/application.md) - 应用管理 -- [系统信息](system/sysinfo.md) - 设备信息 -- [设置](system/settings.md) - 系统设置 -- [唤醒锁](system/wakelock.md) - 唤醒锁控制 -- [QPython 接口](system/qpyinterface.md) - 脚本执行 -- [Activity 结果](system/activityresult.md) - Activity 结果处理 - -### [硬件](hardware/) -- [蓝牙](hardware/bluetooth.md) - 蓝牙操作 -- [相机](hardware/camera.md) - 拍照和录像 -- [音频/录音器](hardware/recorder.md) - 录音 -- [网络摄像头](hardware/webcam.md) - MJPEG 流 -- [USB 串口](hardware/usbserial.md) - USB 主机串口 - -### [连接](connectivity/) -- [WiFi](connectivity/wifi.md) - WiFi 操作 -- [位置](connectivity/location.md) - GPS 和位置 -- [短信](connectivity/sms.md) - 短信操作 -- [电话](connectivity/phone.md) - 打电话和来电信息 -- [联系人](connectivity/contacts.md) - 联系人管理 -- [信号强度](connectivity/signalstrength.md) - 信号监控 -- [FTP 服务器](connectivity/ftp.md) - 内置 FTP 服务器 - -### [存储](storage/) -- [DocumentFile](storage/documentfile.md) - 文件操作 -- [剪贴板](storage/clipboard.md) - 剪贴板操作 -- [偏好设置](storage/preferences.md) - 共享偏好设置 - -### [媒体](media/) -- [媒体播放器](media/mediaplayer.md) - 音频/视频播放 -- [图像处理](media/image.md) - 图像操作 - -### [特殊功能](special/) -- [加密](special/cipher.md) - 加密/解密 -- [PGPT AI](special/pgptai.md) - AI 语音服务 - -## 结果对象 - -大多数 QSL4A 方法返回一个带有以下属性的 Result 命名元组: -- `id` - 请求 ID -- `result` - 实际结果数据 -- `error` - 如果失败则为错误消息 - -```python -result = droid.getClipboard() -if result.error is None: - print(result.result) -else: - print(f"错误:{result.error}") -``` diff --git a/source/zh/qsl4a/media/image.md b/source/zh/qsl4a/media/image.md deleted file mode 100644 index f3d533c..0000000 --- a/source/zh/qsl4a/media/image.md +++ /dev/null @@ -1,94 +0,0 @@ -# 图像处理 API - -压缩和处理图像。 - -## 图像压缩 - -### imageCompress() -压缩图像文件。 - -```python -imageCompress(srcPath, destPath, targetByteSize=0, targetWidth=0, targetHeight=0) -``` - -**参数:** -- `srcPath` (str): 源图像路径 -- `destPath` (str): 输出路径 -- `targetByteSize` (int): 目标文件大小(字节)(0 = 无限制) -- `targetWidth` (int): 目标宽度(0 = 原始) -- `targetHeight` (int): 目标高度(0 = 原始) - -**返回:** 压缩后的图像路径 - -## 截图 - -### imageReaderGetScreenShot() -截取屏幕。 - -```python -imageReaderGetScreenShot(path=None, delayMilliSec=1000) -``` - -**参数:** -- `path` (str): 保存路径 -- `delayMilliSec` (int): 拍摄前延迟 - -**返回:** 截图路径 - -## 视频播放 - -### videoPlay() -在全屏模式下播放视频文件。 - -```python -videoPlay(path, wait=True) -``` - -**参数:** -- `path` (str): 视频文件路径 -- `wait` (bool): 等待播放完成(默认:True) - -## 条码扫描 - -### scanBarcodeFromImage() -从图像文件扫描条码/二维码。 - -```python -scanBarcodeFromImage(path, compressRatio=0, x=0, y=0, width=0, height=0) -``` - -**参数:** -- `path` (str): 图像文件路径 -- `compressRatio` (int): 压缩比(0 = 不压缩) -- `x`, `y`, `width`, `height` (int): 要扫描的区域(0 = 完整图像) - -**返回:** 扫描的条码内容 - -## 使用示例 - -```python -import androidhelper - -droid = androidhelper.Android() - -# 截图 -ss_path = droid.imageReaderGetScreenShot("/sdcard/screenshot.png", 500).result - -# 压缩图像 -compressed = droid.imageCompress( - "/sdcard/large_photo.jpg", - "/sdcard/compressed.jpg", - targetByteSize=102400, # 目标约 100KB - targetWidth=1920, - targetHeight=1080 -).result -print(f"Saved: {compressed}") - -# 播放视频 -video_path = "/sdcard/movie.mp4" -droid.videoPlay(video_path, wait=True) - -# 从图像扫描条码 -result = droid.scanBarcodeFromImage("/sdcard/qr_code.png").result -print(f"Barcode: {result}") -``` diff --git a/source/zh/qsl4a/media/mediaplayer.md b/source/zh/qsl4a/media/mediaplayer.md deleted file mode 100644 index c9eccd7..0000000 --- a/source/zh/qsl4a/media/mediaplayer.md +++ /dev/null @@ -1,152 +0,0 @@ -# 媒体播放器 API - -控制音频和视频播放。 - -## 播放控制 - -### mediaPlay() -播放媒体文件。 - -```python -mediaPlay(url, tag="default", play=True) -``` - -**参数:** -- `url` (str): 媒体文件路径或 URL -- `tag` (str): 播放器标识符 -- `play` (bool): 自动开始播放 - -### mediaPlayStart() -开始播放。 - -```python -mediaPlayStart(tag="default") -``` - -### mediaPlayPause() -暂停播放。 - -```python -mediaPlayPause(tag="default") -``` - -### mediaPlayClose() -关闭播放器。 - -```python -mediaPlayClose(tag="default") -``` - -### mediaPlaySeek() -跳转到位置。 - -```python -mediaPlaySeek(msec, tag="default") -``` - -**参数:** -- `msec` (int): 位置(毫秒) - -### mediaPlaySetLooping() -设置循环模式。 - -```python -mediaPlaySetLooping(enabled, tag="default") -``` - -## 播放器信息 - -### mediaPlayInfo() -获取播放信息。 - -```python -mediaPlayInfo(tag="default") -``` - -**返回:** 包含时长、位置等的字典 - -### mediaIsPlaying() -检查是否正在播放。 - -```python -mediaIsPlaying(tag="default") -``` - -**返回:** True/False - -### mediaPlayList() -列出活动播放器。 - -```python -mediaPlayList() -``` - -## 音量控制 - -### getMediaVolume() -获取媒体音量。 - -```python -getMediaVolume() -``` - -**返回:** 音量级别(0-15) - -### getMaxMediaVolume() -获取最大媒体音量。 - -```python -getMaxMediaVolume() -``` - -### getRingerVolume() -获取铃声音量。 - -```python -getRingerVolume() -``` - -### getMaxRingerVolume() -获取最大铃声音量。 - -```python -getMaxRingerVolume() -``` - -## 视频播放 - -### videoPlay() -全屏播放视频。 - -```python -videoPlay(path, wait=True) -``` - -**参数:** -- `path` (str): 视频文件路径 -- `wait` (bool): 等待播放完成 - -## 使用示例 - -```python -import androidhelper - -droid = androidhelper.Android() - -# 播放音频 -droid.mediaPlay("/sdcard/music.mp3", tag="music") - -# 检查状态 -if droid.mediaIsPlaying("music").result: - info = droid.mediaPlayInfo("music").result - print(f"Playing: {info}") - -# 跳转到 30 秒 -droid.mediaPlaySeek(30000, "music") - -# 关闭 -droid.mediaPlayClose("music") - -# 播放视频 -droid.videoPlay("/sdcard/movie.mp4", wait=True) -``` diff --git a/source/zh/qsl4a/special/cipher.md b/source/zh/qsl4a/special/cipher.md deleted file mode 100644 index d2079bb..0000000 --- a/source/zh/qsl4a/special/cipher.md +++ /dev/null @@ -1,143 +0,0 @@ -# 密码 API - -用于安全数据存储的加密和解密工具。 - -## 初始化 - -### cipherInit() -使用加密密钥和算法初始化密码器。 - -```python -cipherInit(key, algorithm="AES/CBC/PKCS5Padding", encodingFormat="", initialVector="") -``` - -**参数:** -- `key` (str or bytes): 加密密钥 -- `algorithm` (str): 密码算法(默认:"AES/CBC/PKCS5Padding") -- `encodingFormat` (str): 编码格式 -- `initialVector` (str or bytes): 加密的初始向量 - -**返回:** 初始化结果 - -**注意:** 必须在任何加密/解密操作之前调用。 - -## 加密方法 - -### encryptString() -加密字符串。 - -```python -encryptString(plainText) -``` - -**参数:** -- `plainText` (str): 要加密的文本 - -**返回:** 加密的字符串 - -### encryptBytes() -加密字节数据。 - -```python -encryptBytes(data) -``` - -**参数:** -- `data` (bytes): 要加密的数据 - -**返回:** 加密的字节 - -### encryptStringToFile() -加密字符串到文件。 - -```python -encryptStringToFile(plainText, filePath) -``` - -**参数:** -- `plainText` (str): 要加密的文本 -- `filePath` (str): 输出文件路径 - -### encryptBytesToFile() -加密字节到文件。 - -```python -encryptBytesToFile(data, filePath) -``` - -## 解密方法 - -### decryptString() -解密为字符串。 - -```python -decryptString(cipherText) -``` - -**参数:** -- `cipherText` (str): 加密的文本 - -**返回:** 解密的字符串 - -### decryptBytes() -解密为字节。 - -```python -decryptBytes(data) -``` - -**返回:** 解密的字节 - -### decryptFileToString() -解密文件到字符串。 - -```python -decryptFileToString(filePath) -``` - -### decryptFileToBytes() -解密文件到字节。 - -```python -decryptFileToBytes(filePath) -``` - -### decryptFile() -解密文件到文件。 - -```python -decryptFile(srcPath, destPath) -``` - -## 使用示例 - -```python -import androidhelper - -droid = androidhelper.Android() - -# 使用加密密钥初始化密码器 -droid.cipherInit("my_secret_key_1234") - -# 加密字符串 -encrypted = droid.encryptString("Secret message!").result -print(f"Encrypted: {encrypted}") - -# 解密字符串 -decrypted = droid.decryptString(encrypted).result -print(f"Decrypted: {decrypted}") - -# 加密到文件 -droid.encryptStringToFile("My secret data", "/sdcard/secret.dat") - -# 从文件解密 -data = droid.decryptFileToString("/sdcard/secret.dat").result -print(data) - -# 使用自定义算法和 IV 初始化 -droid.cipherInit( - key="my_key", - algorithm="AES/CBC/PKCS5Padding", - initialVector="0123456789abcdef" -) -``` diff --git a/source/zh/qsl4a/special/pgptai.md b/source/zh/qsl4a/special/pgptai.md deleted file mode 100644 index 1e7ca9b..0000000 --- a/source/zh/qsl4a/special/pgptai.md +++ /dev/null @@ -1,100 +0,0 @@ -# PGPT AI API - -语音转文本和 AI 服务集成。 - -## 先决条件 - -```bash -pip install pgptAI -``` - -## 语音识别 - -### speechToText() -将语音转换为文本。 - -```python -speechToText(RecordSecond=10, AmrFile=None, Language=None) -``` - -**参数:** -- `RecordSecond` (int): 录制时长(秒) -- `AmrFile` (str, optional): 现有音频文件路径 -- `Language` (str, optional): 语言代码('en'、'zh') - -**返回:** 转录的文本 - -## 文本转语音 - -### textToSpeech() -将文本转换为语音并可选地播放。 - -```python -textToSpeech(Text, AutoPlay=True, WavFile=None, VoiceName=None) -``` - -**参数:** -- `Text` (str): 要转换为语音的文本 -- `AutoPlay` (bool): 自动播放生成的音频(默认:True) -- `WavFile` (str, optional): 保存 WAV 文件的路径。如果为 None,则使用临时文件 -- `VoiceName` (str, optional): 要使用的语音名称(例如 'en-US-JennyNeural'、'zh-CN-XiaoxiaoNeural') - -**返回:** 包含语音合成结果的字典,包括: - - `text`: 输入文本 - - `url`: 下载音频文件的 URL - - `WavFile`: 保存的 WAV 文件路径(如果本地保存) - -## 配置 - -API 使用以下配置文件中的配置:`/storage/emulated/0/Android/data/org.qpython.qpy/files/apigpt.conf`: - -```ini -[speech] -speech_key = your_api_key -``` - -默认语音设置: -- 英语:`en-US-JennyNeural` -- 中文:`zh-CN-XiaoxiaoNeural` - -## 使用示例 - -```python -import androidhelper - -droid = androidhelper.Android() - -# 录制并转录 -print("Recording for 5 seconds...") -text = droid.speechToText(RecordSecond=5, Language='en').result -print(f"You said: {text}") - -# 转录现有文件 -text = droid.speechToText(AmrFile="/sdcard/recording.amr").result -print(f"Transcription: {text}") - -# 文本转语音 -droid.textToSpeech("Hello, this is a test message.", AutoPlay=True, Language='en') - -# 使用自定义语音并保存到文件的文本转语音 -result = droid.textToSpeech( - "Welcome to QPython!", - AutoPlay=False, - WavFile="/sdcard/welcome.wav", - VoiceName="en-US-JennyNeural" -).result -print(f"Audio saved to: {result.get('WavFile')}") -``` - -## 类使用 - -```python -from androidhelper.pgptai import pgptai -import androidhelper - -droid = androidhelper.Android() -ai = pgptai(droid) - -# 使用语音识别 -text = ai.speechToText(RecordSecond=10) -``` diff --git a/source/zh/qsl4a/storage/clipboard.md b/source/zh/qsl4a/storage/clipboard.md deleted file mode 100644 index 1379064..0000000 --- a/source/zh/qsl4a/storage/clipboard.md +++ /dev/null @@ -1,41 +0,0 @@ -# 剪贴板 API - -复制和粘贴文本到系统剪贴板。 - -## 方法 - -### setClipboard() -复制文本到剪贴板。 - -```python -setClipboard(text) -``` - -**参数:** -- `text` (str): 要复制的文本 - -**返回:** 如果成功则为 True - -### getClipboard() -从剪贴板获取文本。 - -```python -getClipboard() -``` - -**返回:** 剪贴板文本 - -## 使用示例 - -```python -import androidhelper - -droid = androidhelper.Android() - -# 复制到剪贴板 -droid.setClipboard("Hello from QPython!") - -# 从剪贴板粘贴 -text = droid.getClipboard().result -print(f"Clipboard: {text}") -``` diff --git a/source/zh/qsl4a/storage/documentfile.md b/source/zh/qsl4a/storage/documentfile.md deleted file mode 100644 index d0fe91f..0000000 --- a/source/zh/qsl4a/storage/documentfile.md +++ /dev/null @@ -1,205 +0,0 @@ -# DocumentFile API - -使用 SAF(存储访问框架)进行文件操作,支持 Android 4.4+。 - -## 目录操作 - -### documentFileMkdir() -创建目录。 - -```python -documentFileMkdir(Dir) -``` - -**参数:** -- `Dir` (str): 目录路径 - -**返回:** 如果成功则为 True - -### documentFileListFiles() -列出目录中的文件。 - -```python -documentFileListFiles(Folder) -``` - -**返回:** 文件列表 - -## 文件操作 - -### documentFileExists() -检查文件或目录是否存在。 - -```python -documentFileExists(path) -``` - -**参数:** -- `path` (str): 文件或目录路径 - -**返回:** 如果存在则为 True,否则为 False - -### documentFileIsFile() -检查路径是否是文件。 - -```python -documentFileIsFile(path) -``` - -**参数:** -- `path` (str): 要检查的路径 - -**返回:** 如果是文件则为 True,如果不是文件则为 False,如果不存在则为 None - -### documentFileIsDirectory() -检查路径是否是目录。 - -```python -documentFileIsDirectory(path) -``` - -**参数:** -- `path` (str): 要检查的路径 - -**返回:** 如果是目录则为 True,如果不是目录则为 False,如果不存在则为 None - -### documentFileDelete() -删除文件或目录。 - -```python -documentFileDelete(FileOrTree) -``` - -**返回:** 如果成功则为 True - -### documentFileRenameTo() -重命名或移动文件。 - -```python -documentFileRenameTo(Src, Dest) -``` - -**返回:** 如果成功则为 True - -### documentFileCopy() -复制文件。 - -```python -documentFileCopy(SrcFileOrTree, DestFileOrTree) -``` - -## 流操作 - -### documentFileInputStream() -读取文件内容。 - -```python -documentFileInputStream(srcFile, EncodingFormat="", skip=None, length=None) -``` - -**参数:** -- `srcFile` (str): 源文件 -- `EncodingFormat` (str): "UTF-8"、"GBK"、"Base64" 或空字符串表示字节 -- `skip` (int): 从开头跳过的字节数 -- `length` (int): 读取长度 - -**返回:** 文件内容 - -### documentFileOutputStream() -写入文件内容。 - -```python -documentFileOutputStream(destFile, src, EncodingFormat="", append=None) -``` - -**参数:** -- `destFile` (str): 目标文件 -- `src`: 要写入的数据 -- `EncodingFormat` (str): 编码格式 -- `append` (bool): 追加模式 - -## 文件信息 - -### documentFileLength() -获取文件大小(字节)。 - -```python -documentFileLength(path) -``` - -**参数:** -- `path` (str): 文件路径 - -**返回:** 文件大小(字节)(如果不存在则为 0) - -### documentFileLastModified() -获取最后修改时间。 - -```python -documentFileLastModified(path) -``` - -**参数:** -- `path` (str): 文件路径 - -**返回:** 时间戳(如果不存在则为 0) - -### documentFileGetStat() -获取全面的文件统计信息。 - -```python -documentFileGetStat(path) -``` - -**参数:** -- `path` (str): 文件路径 - -**返回:** 包含长度、最后修改时间和读/写权限的字典,如果不存在则为 None - -## URI 操作 - -### documentFileGetUri() -从路径获取 URI。 - -```python -documentFileGetUri(path, isDirectory=None) -``` - -### documentFileShowOpen() -显示文件选择器。 - -```python -documentFileShowOpen() -``` - -**返回:** 选择的文件 URI - -## 使用示例 - -```python -import androidhelper - -droid = androidhelper.Android() - -# 创建目录 -droid.documentFileMkdir("/sdcard/MyFolder") - -# 列出文件 -files = droid.documentFileListFiles("/sdcard").result -for f in files: - print(f) - -# 读取文件 -content = droid.documentFileInputStream( - "/sdcard/test.txt", - EncodingFormat="UTF-8" -).result -print(content) - -# 写入文件 -droid.documentFileOutputStream( - "/sdcard/output.txt", - "Hello World", - EncodingFormat="UTF-8" -) -``` diff --git a/source/zh/qsl4a/storage/preferences.md b/source/zh/qsl4a/storage/preferences.md deleted file mode 100644 index 7e5143a..0000000 --- a/source/zh/qsl4a/storage/preferences.md +++ /dev/null @@ -1,81 +0,0 @@ -# 偏好设置 API - -使用 Android SharedPreferences 存储和检索数据。 - -## 偏好设置方法 - -### prefGetValue() -从共享偏好设置读取值。 - -```python -prefGetValue(key, filename=None) -``` - -**参数:** -- `key` (str): 偏好设置键 -- `filename` (str, optional): 偏好设置文件名 - -**返回:** 存储的值(任意类型) - -### prefPutValue() -写入值到共享偏好设置。 - -```python -prefPutValue(key, value, filename=None) -``` - -**参数:** -- `key` (str): 偏好设置键 -- `value` (object): 要存储的值 -- `filename` (str, optional): 偏好设置文件名 - -### prefGetAll() -获取所有偏好设置值。 - -```python -prefGetAll(filename=None) -``` - -**参数:** -- `filename` (str, optional): 偏好设置文件名 - -**返回:** 所有偏好的映射 - -### prefRemoveValue() -从共享偏好设置中移除值。 - -```python -prefRemoveValue(key, filename=None) -``` - -**参数:** -- `key` (str): 要移除的偏好设置键 -- `filename` (str, optional): 偏好设置文件名 - -## 使用示例 - -```python -import androidhelper - -droid = androidhelper.Android() - -# 存储值 -droid.prefPutValue("username", "alice") -droid.prefPutValue("score", 100) -droid.prefPutValue("enabled", True) - -# 读取特定值 -username = droid.prefGetValue("username").result -print(f"Username: {username}") - -# 获取所有偏好设置 -all_prefs = droid.prefGetAll().result -print(f"All prefs: {all_prefs}") - -# 移除值 -droid.prefRemoveValue("score") - -# 使用自定义文件名 -droid.prefPutValue("token", "abc123", filename="auth.prefs") -token = droid.prefGetValue("token", filename="auth.prefs").result -``` diff --git a/source/zh/qsl4a/system/activityresult.md b/source/zh/qsl4a/system/activityresult.md deleted file mode 100644 index 845294c..0000000 --- a/source/zh/qsl4a/system/activityresult.md +++ /dev/null @@ -1,230 +0,0 @@ -# Activity Result API - -为通过 `startActivityForResult` 启动的脚本设置 activity 结果。 - -## 结果方法 - -### setResultBoolean() -设置布尔结果。 - -```python -setResultBoolean(resultCode, resultValue) -``` - -**参数:** -- `resultCode` (int): 结果代码 -- `resultValue` (bool): 布尔结果值 - -### setResultByte() -设置字节结果。 - -```python -setResultByte(resultCode, resultValue) -``` - -**参数:** -- `resultCode` (int): 结果代码 -- `resultValue` (int): 字节结果值 - -### setResultShort() -设置短整数结果。 - -```python -setResultShort(resultCode, resultValue) -``` - -**参数:** -- `resultCode` (int): 结果代码 -- `resultValue` (int): 短整数结果值 - -### setResultChar() -设置字符结果。 - -```python -setResultChar(resultCode, resultValue) -``` - -**参数:** -- `resultCode` (int): 结果代码 -- `resultValue` (str): 字符结果值 - -### setResultInteger() -设置整数结果。 - -```python -setResultInteger(resultCode, resultValue) -``` - -**参数:** -- `resultCode` (int): 结果代码 -- `resultValue` (int): 整数结果值 - -### setResultLong() -设置长整数结果。 - -```python -setResultLong(resultCode, resultValue) -``` - -**参数:** -- `resultCode` (int): 结果代码 -- `resultValue` (int): 长整数结果值 - -### setResultFloat() -设置浮点数结果。 - -```python -setResultFloat(resultCode, resultValue) -``` - -**参数:** -- `resultCode` (int): 结果代码 -- `resultValue` (float): 浮点数结果值 - -### setResultDouble() -设置双精度浮点数结果。 - -```python -setResultDouble(resultCode, resultValue) -``` - -**参数:** -- `resultCode` (int): 结果代码 -- `resultValue` (float): 双精度浮点数结果值 - -### setResultString() -设置字符串结果。 - -```python -setResultString(resultCode, resultValue) -``` - -**参数:** -- `resultCode` (int): 结果代码 -- `resultValue` (str): 字符串结果值 - -### setResultBooleanArray() -设置布尔数组结果。 - -```python -setResultBooleanArray(resultCode, resultValue) -``` - -**参数:** -- `resultCode` (int): 结果代码 -- `resultValue` (list): 布尔数组 - -### setResultByteArray() -设置字节数组结果。 - -```python -setResultByteArray(resultCode, resultValue) -``` - -**参数:** -- `resultCode` (int): 结果代码 -- `resultValue` (list): 字节数组 - -### setResultShortArray() -设置短整数数组结果。 - -```python -setResultShortArray(resultCode, resultValue) -``` - -**参数:** -- `resultCode` (int): 结果代码 -- `resultValue` (list): 短整数数组 - -### setResultCharArray() -设置字符数组结果。 - -```python -setResultCharArray(resultCode, resultValue) -``` - -**参数:** -- `resultCode` (int): 结果代码 -- `resultValue` (list): 字符数组 - -### setResultIntegerArray() -设置整数数组结果。 - -```python -setResultIntegerArray(resultCode, resultValue) -``` - -**参数:** -- `resultCode` (int): 结果代码 -- `resultValue` (list): 整数数组 - -### setResultLongArray() -设置长整数数组结果。 - -```python -setResultLongArray(resultCode, resultValue) -``` - -**参数:** -- `resultCode` (int): 结果代码 -- `resultValue` (list): 长整数数组 - -### setResultFloatArray() -设置浮点数数组结果。 - -```python -setResultFloatArray(resultCode, resultValue) -``` - -**参数:** -- `resultCode` (int): 结果代码 -- `resultValue` (list): 浮点数数组 - -### setResultDoubleArray() -设置双精度浮点数数组结果。 - -```python -setResultDoubleArray(resultCode, resultValue) -``` - -**参数:** -- `resultCode` (int): 结果代码 -- `resultValue` (list): 双精度浮点数数组 - -### setResultStringArray() -设置字符串数组结果。 - -```python -setResultStringArray(resultCode, resultValue) -``` - -**参数:** -- `resultCode` (int): 结果代码 -- `resultValue` (list): 字符串数组 - -### setResultSerializable() -设置可序列化结果。 - -```python -setResultSerializable(resultCode, resultValue) -``` - -**参数:** -- `resultCode` (int): 结果代码 -- `resultValue`: 可序列化结果值 - -## 使用示例 - -```python -import androidhelper - -droid = androidhelper.Android() - -# 执行 activity 后,设置结果 -# 示例:返回成功及数据 -droid.setResultInteger(0, 200) # RESULT_OK -droid.setResultString(0, "Operation completed successfully") - -# 返回数组结果 -droid.setResultIntegerArray(0, [1, 2, 3, 4, 5]) -``` diff --git a/source/zh/qsl4a/system/application.md b/source/zh/qsl4a/system/application.md deleted file mode 100644 index a5d8b4f..0000000 --- a/source/zh/qsl4a/system/application.md +++ /dev/null @@ -1,269 +0,0 @@ -# 应用管理 - -管理应用程序、启动应用和查询系统信息。 - -## 应用程序信息 - -### getApplicationInfo() -获取应用信息。 - -```python -getApplicationInfo(packageName=None) -``` - -**参数:** -- `packageName` (str): 包名(None = 当前应用) - -**返回:** 应用信息字典 - -### getInstalledPackages() -获取已安装包列表。 - -```python -getInstalledPackages(flag=4) -``` - -**参数:** -- `flag` (int): 包标志过滤器(默认:4) - -**返回:** 已安装包列表 - -### getRunningPackages() -列出正在运行的包。 - -```python -getRunningPackages() -``` - -**返回:** 包名列表 - -### getLaunchablePackages() -获取可启动包列表。 - -```python -getLaunchablePackages(needClassName=False) -``` - -**参数:** -- `needClassName` (bool): 包含主 activity 类名(默认:False) - -**返回:** 可启动包名列表或包含类名的字典 - -## 应用控制 - -### launch() -启动应用程序。 - -```python -launch(classname=None, packagename=None, wait=True) -``` - -**参数:** -- `classname` (str): 主 activity 类名 -- `packagename` (str): 包名 -- `wait` (bool): 等待启动完成(默认:True) - -**返回:** 启动结果 - -### forceStopPackage() -强制停止应用程序。 - -```python -forceStopPackage(packageName) -``` - -**参数:** -- `packageName` (str): 要停止的包名 - -## 版本信息 - -### getPackageVersion() -获取应用版本名称。 - -```python -getPackageVersion(packageName) -``` - -**返回:** 版本字符串(例如 "3.2.1") - -### getPackageVersionCode() -获取应用版本代码。 - -```python -getPackageVersionCode(packageName) -``` - -**返回:** 版本代码整数 - -### getConstants() -获取类常量。 - -```python -getConstants(classname) -``` - -**参数:** -- `classname` (str): 完整类名 - -**返回:** 常量名和值的字典 - -## 系统功能 - -### backgroundProtect() -启用或禁用应用的后台保护。 - -```python -backgroundProtect(enabled=True) -``` - -**参数:** -- `enabled` (bool): True 启用保护,False 禁用 - -### createScriptShortCut() -为脚本创建主屏幕快捷方式。 - -```python -createScriptShortCut(scriptPath, label=None, iconPath=None, scriptArg=None) -``` - -**参数:** -- `scriptPath` (str): Python 脚本路径 -- `label` (str, optional): 快捷方式标签 -- `iconPath` (str, optional): 图标图像路径 -- `scriptArg` (str, optional): 传递给脚本的参数 - -## 设备信息 - -### getAndroidID() -获取 Android 设备 ID。 - -```python -getAndroidID() -``` - -**返回:** 唯一的 Android 设备 ID 字符串 - -### getSysInfo() -获取系统信息。 - -```python -getSysInfo() -``` - -**返回:** 包含系统详情的字典 - -### getLocale() -获取设备语言环境。 - -```python -getLocale() -``` - -**返回:** 语言环境字符串(例如 "en_US") - -### getHarmonyOsInformation() -如果在 HarmonyOS 上运行,获取 HarmonyOS 信息。 - -```python -getHarmonyOsInformation() -``` - -**返回:** HarmonyOS 版本信息或 None - -### isExternalStorageManager() -检查应用是否有外部存储管理器权限。 - -```python -isExternalStorageManager() -``` - -**返回:** 如果有权限则为 True - -## 内存和显示 - -### getMemoryInfo() -获取内存信息。 - -```python -getMemoryInfo() -``` - -**返回:** 包含内存统计的字典 - -### getScreenInfo() -获取屏幕信息。 - -```python -getScreenInfo() -``` - -**返回:** 包含宽度、高度、密度的字典 - -## 权限 - -### checkPermissions() -检查当前应用权限。 - -```python -checkPermissions() -``` - -**返回:** 权限及其状态的字典 - -### requestPermissions() -向用户请求权限。 - -```python -requestPermissions(permissions=None) -``` - -**参数:** -- `permissions` (list, optional): 要请求的权限列表。如果为 None,则请求所有需要的权限。 - -**返回:** 权限请求结果 - -## 系统操作 - -### showScreenLock() -显示屏幕锁定(PIN/图案/密码输入)。 - -```python -showScreenLock() -``` - -## 使用示例 - -```python -import androidhelper - -droid = androidhelper.Android() - -# 获取系统信息 -info = droid.getSysInfo().result -print(f"System: {info}") - -# 获取应用版本 -version = droid.getPackageVersion("org.qpython.qpy").result -print(f"QPython version: {version}") - -# 列出已安装应用 -apps = droid.getInstalledPackages().result -print(f"Installed apps: {len(apps)}") - -# 启动应用 -droid.launch(packagename="com.android.settings") - -# 检查权限 -perms = droid.checkPermissions().result -print(f"Permissions: {perms}") - -# 请求存储权限 -droid.requestPermissions(["android.permission.READ_EXTERNAL_STORAGE"]) - -# 创建快捷方式 -droid.createScriptShortCut( - "/sdcard/my_script.py", - label="My Script", - iconPath="/sdcard/icon.png" -) -``` diff --git a/source/zh/qsl4a/system/battery.md b/source/zh/qsl4a/system/battery.md deleted file mode 100644 index 27efd2c..0000000 --- a/source/zh/qsl4a/system/battery.md +++ /dev/null @@ -1,91 +0,0 @@ -# 电池 API - -监控设备电池状态和健康状况。 - -## 方法 - -### readBatteryData() -获取完整的电池信息。 - -```python -readBatteryData() -``` - -**返回:** 包含电池数据的字典 - -### batteryStartMonitoring() -开始电池监控。 - -```python -batteryStartMonitoring() -``` - -### batteryStopMonitoring() -停止电池监控。 - -```python -batteryStopMonitoring() -``` - -### batteryGetLevel() -获取电池百分比。 - -```python -batteryGetLevel() -``` - -**返回:** 整数(0-100) - -### batteryGetStatus() -获取充电状态。 - -```python -batteryGetStatus() -``` - -**返回:** -- 1: 未知 -- 2: 充电中 -- 3: 放电中 -- 4: 未充电 -- 5: 已充满 - -### batteryGetPlugType() -获取电源类型。 - -```python -batteryGetPlugType() -``` - -**返回:** -- -1: 未知 -- 0: 未插电 -- 1: AC 充电器 -- 2: USB 端口 - -### batteryGetHealth() -获取电池健康状况。 - -```python -batteryGetHealth() -``` - -**返回:** -- 1: 未知 -- 2: 良好 -- 3: 过热 -- 4: 损坏 -- 5: 过压 -- 6: 未指定的故障 - -## 使用示例 - -```python -import androidhelper - -droid = androidhelper.Android() - -# 获取电池电量 -level = droid.batteryGetLevel().result -print(f"Battery: {level}%") -``` diff --git a/source/zh/qsl4a/system/qpyinterface.md b/source/zh/qsl4a/system/qpyinterface.md deleted file mode 100644 index c523604..0000000 --- a/source/zh/qsl4a/system/qpyinterface.md +++ /dev/null @@ -1,131 +0,0 @@ -# QPython 接口 API - -从其他应用执行 QPython 脚本和管理共享变量。 - -## 脚本执行方法 - -### executeQPy() -执行 QPython 脚本。 - -```python -executeQPy(path="", arg=None) -``` - -**参数:** -- `path` (str): 脚本文件路径 -- `arg` (str, optional): 命令行参数 - -**返回:** 如果启动成功则为 True - -### executeQPyAsSrv() -作为服务执行 QPython 脚本。 - -```python -executeQPyAsSrv(path=None) -``` - -**参数:** -- `path` (str, optional): 脚本文件路径 - -**返回:** 如果启动成功则为 True - -### executeQPyCode() -直接执行 Python 代码。 - -```python -executeQPyCode(code=None) -``` - -**参数:** -- `code` (str, optional): 要执行的 Python 代码 - -**返回:** 如果启动成功则为 True - -### executeQPyCodeAsSrv() -作为服务执行 Python 代码。 - -```python -executeQPyCodeAsSrv(code=None) -``` - -**参数:** -- `code` (str, optional): 要执行的 Python 代码 - -**返回:** 如果启动成功则为 True - -## 共享变量 - -共享变量允许 QPython 与其他应用之间进行通信。 - -### sharedVariableSet() -设置 Java 共享变量。 - -```python -sharedVariableSet(key, value) -``` - -**参数:** -- `key` (str): 变量名 -- `value` (str): 变量值 - -**返回:** 存储的值 - -### sharedVariableGet() -获取 Java 共享变量。 - -```python -sharedVariableGet(key) -``` - -**参数:** -- `key` (str): 变量名 - -**返回:** 存储的值 - -### sharedVariableRemove() -移除 Java 共享变量。 - -```python -sharedVariableRemove(key) -``` - -**参数:** -- `key` (str): 要移除的变量名 - -**返回:** 被移除的值 - -### getLastLog() -获取 QPython 的最后日志输出。 - -```python -getLastLog() -``` - -**返回:** 字符串日志内容 - -## 使用示例 - -```python -import androidhelper - -droid = androidhelper.Android() - -# 执行脚本 -droid.executeQPy("/sdcard/my_script.py", arg="test") - -# 直接执行代码 -code = "print('Hello from QPython!')" -droid.executeQPyCode(code) - -# 使用共享变量 -droid.sharedVariableSet("username", "alice") -username = droid.sharedVariableGet("username").result -print(f"Username: {username}") - -# 移除变量 -droid.sharedVariableRemove("username") - -# 获取最近日志 -log = droid.getLastLog().result -print(f"Log: {log}") -``` diff --git a/source/zh/qsl4a/system/sensors.md b/source/zh/qsl4a/system/sensors.md deleted file mode 100644 index b2d0d92..0000000 --- a/source/zh/qsl4a/system/sensors.md +++ /dev/null @@ -1,127 +0,0 @@ -# 传感器 API - -访问设备传感器,包括加速度计、陀螺仪、磁力计等。 - -## 方法 - -### startSensingTimed() -开始传感器监控并设置时间间隔。 - -```python -startSensingTimed(sensorNumber, delayTime) -``` - -**参数:** -- `sensorNumber` (int): 传感器 ID(通常为 1-3) -- `delayTime` (int): 读取之间的延迟(毫秒) - -### startSensingThreshold() -开始带阈值触发的传感器监控。 - -```python -startSensingThreshold(sensorNumber, threshold, axis) -``` - -**参数:** -- `sensorNumber` (int): 传感器 ID -- `threshold` (float): 触发阈值 -- `axis` (int): 要监控的轴(0=X,1=Y,2=Z) - -### stopSensing() -停止所有传感器监控。 - -```python -stopSensing() -``` - -### readSensors() -读取当前传感器数据。 - -```python -readSensors() -``` - -**返回:** 传感器数据字典 - -### sensorsReadAccelerometer() -读取加速度计值。 - -```python -sensorsReadAccelerometer() -``` - -**返回:** 列表 [X, Y, Z],单位 m/s² - -### sensorsReadGyroscope() -读取陀螺仪值。 - -```python -sensorsReadGyroscope() -``` - -**返回:** 列表 [X, Y, Z],单位 rad/s - -### sensorsReadMagnetometer() -读取磁场值。 - -```python -sensorsReadMagnetometer() -``` - -**返回:** 列表 [X, Y, Z],单位 μT - -### sensorsReadOrientation() -读取设备方向。 - -```python -sensorsReadOrientation() -``` - -**返回:** 列表 [azimuth, pitch, roll],单位度 - -### sensorsGetLight() -读取光传感器值。 - -```python -sensorsGetLight() -``` - -**返回:** 光级别(lux) - -### sensorsGetStepCounter() -读取步计数器。 - -```python -sensorsGetStepCounter() -``` - -**返回:** 步数 - -### sensorsGetAccuracy() -获取当前传感器精度。 - -```python -sensorsGetAccuracy() -``` - -**返回:** 精度值(0-3:UNRELIABLE、ACCURACY_LOW、ACCURACY_MEDIUM、ACCURACY_HIGH) - -## 使用示例 - -```python -import androidhelper -import time - -droid = androidhelper.Android() - -# 开始感知 -droid.startSensingTimed(1, 250) - -# 读取传感器 10 次 -for i in range(10): - accel = droid.sensorsReadAccelerometer().result - print(f"Accel: X={accel[0]:.2f}, Y={accel[1]:.2f}, Z={accel[2]:.2f}") - time.sleep(0.5) - -droid.stopSensing() -``` diff --git a/source/zh/qsl4a/system/settings.md b/source/zh/qsl4a/system/settings.md deleted file mode 100644 index 52f1fb8..0000000 --- a/source/zh/qsl4a/system/settings.md +++ /dev/null @@ -1,234 +0,0 @@ -# 设置 API - -控制系统设置,包括屏幕、声音和网络设置。 - -## 屏幕设置 - -### setScreenTimeout() -设置屏幕超时值。 - -```python -setScreenTimeout(value) -``` - -**参数:** -- `value` (int): 屏幕超时时间(秒) - -**返回:** 之前的超时值 - -### getScreenTimeout() -获取当前屏幕超时。 - -```python -getScreenTimeout() -``` - -**返回:** 当前屏幕超时时间(秒) - -### getScreenBrightness() -获取屏幕亮度值。 - -```python -getScreenBrightness() -``` - -**返回:** 亮度值(0-255) - -### setScreenBrightness() -设置屏幕亮度。 - -```python -setScreenBrightness(value=None) -``` - -**参数:** -- `value` (int, optional): 亮度值(0-255),或 None 表示自动 - -**返回:** 之前的亮度值 - -### checkScreenOn() -检查屏幕是否亮着。 - -```python -checkScreenOn() -``` - -**返回:** 如果屏幕亮着则为 True,否则为 False - -## 飞行模式 - -### checkAirplaneMode() -检查飞行模式是否启用。 - -```python -checkAirplaneMode() -``` - -**返回:** 如果飞行模式开着则为 True - -## 铃声设置 - -### checkRingerSilentMode() -检查铃声是否处于静音模式。 - -```python -checkRingerSilentMode() -``` - -**返回:** 如果静音模式开着则为 True - -### toggleRingerSilentMode() -切换铃声静音模式。 - -```python -toggleRingerSilentMode(enabled=None) -``` - -**参数:** -- `enabled` (bool, optional): True 启用,False 禁用,None 切换 - -**返回:** 新状态 - -### toggleVibrateMode() -切换振动模式。 - -```python -toggleVibrateMode(enabled=None, ringer=None) -``` - -**参数:** -- `enabled` (bool, optional): 切换振动开/关 -- `ringer` (bool, optional): 应用于铃声模式 - -**返回:** 新状态 - -### getVibrateMode() -获取振动模式设置。 - -```python -getVibrateMode(ringer=None) -``` - -**参数:** -- `ringer` (bool, optional): 检查铃声振动模式 - -**返回:** 如果振动已启用则为 True - -## 音量设置 - -### getRingerVolume() -获取当前铃声音量。 - -```python -getRingerVolume() -``` - -**返回:** 铃声音量级别(通常为 0-7) - -### getMaxRingerVolume() -获取最大铃声音量。 - -```python -getMaxRingerVolume() -``` - -**返回:** 最大铃声音量 - -### setRingerVolume() -设置铃声音量。 - -```python -setRingerVolume(volume) -``` - -**参数:** -- `volume` (int): 音量级别 - -### getMediaVolume() -获取当前媒体音量。 - -```python -getMediaVolume() -``` - -**返回:** 媒体音量级别(通常为 0-15) - -### getMaxMediaVolume() -获取最大媒体音量。 - -```python -getMaxMediaVolume() -``` - -**返回:** 最大媒体音量 - -### setMediaVolume() -设置媒体音量。 - -```python -setMediaVolume(volume) -``` - -**参数:** -- `volume` (int): 音量级别 - -## 系统信息 - -### elapsedRealtimeNanos() -获取自系统启动以来的纳秒数。 - -```python -elapsedRealtimeNanos() -``` - -**返回:** 纳秒数(可用于计时) - -### getTrafficStats() -获取网络流量统计。 - -```python -getTrafficStats(flags=7) -``` - -**参数:** -- `flags` (int): 要检索的统计(默认:7 = 全部) - -**返回:** 包含发送/接收字节的字典 - -### getAppTxBytes() -获取 QPython 应用的发送字节。 - -```python -getAppTxBytes(packageName) -``` - -**参数:** -- `packageName` (str): 包名 - -**返回:** 包含 tx/rx 字节的字典 - -## 使用示例 - -```python -import androidhelper - -droid = androidhelper.Android() - -# 屏幕设置 -current_timeout = droid.getScreenTimeout().result -print(f"Current timeout: {current_timeout}s") -droid.setScreenTimeout(30) - -# 检查屏幕 -if droid.checkScreenOn().result: - print("Screen is on") - -# 音量控制 -media_vol = droid.getMediaVolume().result -print(f"Media volume: {media_vol}") -droid.setMediaVolume(10) - -# 检查飞行模式 -if droid.checkAirplaneMode().result: - print("Airplane mode is on") -``` diff --git a/source/zh/qsl4a/system/sysinfo.md b/source/zh/qsl4a/system/sysinfo.md deleted file mode 100644 index 132c1cc..0000000 --- a/source/zh/qsl4a/system/sysinfo.md +++ /dev/null @@ -1,90 +0,0 @@ -# 系统信息 - -检索设备和系统信息。 - -## 设备信息 - -### getAndroidID() -获取 Android 设备 ID。 - -```python -getAndroidID() -``` - -**返回:** 字符串设备 ID - -### getSysInfo() -获取综合系统信息。 - -```python -getSysInfo() -``` - -**返回:** 包含系统详情的字典 - -### getLocale() -获取设备语言环境设置。 - -```python -getLocale() -``` - -**返回:** 语言环境字符串(例如 "en_US") - -## 内存 - -### getMemoryInfo() -获取 RAM 信息。 - -```python -getMemoryInfo() -``` - -**返回:** 包含内存统计的字典 - -## 显示 - -### getScreenInfo() -获取显示信息。 - -```python -getScreenInfo() -``` - -**返回:** 包含宽度、高度、密度的字典 - -## 标识符 - -### getImei() -获取设备 IMEI。 - -```python -getImei(slotIndex=None) -``` - -### getMeid() -获取设备 MEID。 - -```python -getMeid(slotIndex=None) -``` - -## 使用示例 - -```python -import androidhelper - -droid = androidhelper.Android() - -# 设备 ID -android_id = droid.getAndroidID().result -print(f"Android ID: {android_id}") - -# 屏幕信息 -screen = droid.getScreenInfo().result -print(f"Screen: {screen['width']}x{screen['height']}") - -# 内存 -memory = droid.getMemoryInfo().result -print(f"Memory: {memory}") -``` diff --git a/source/zh/qsl4a/system/wakelock.md b/source/zh/qsl4a/system/wakelock.md deleted file mode 100644 index 5322945..0000000 --- a/source/zh/qsl4a/system/wakelock.md +++ /dev/null @@ -1,78 +0,0 @@ -# WakeLock API - -控制设备唤醒锁以保持 CPU 或屏幕开启。 - -## Wake Lock 类型 - -QSL4A 提供不同类型的唤醒锁: - -| 类型 | 描述 | -|------|-------------| -| Full | CPU 开启,屏幕亮,键盘亮 | -| Partial | 仅 CPU 开启 | -| Bright | CPU 开启,屏幕亮 | -| Dim | CPU 开启,屏幕暗 | - -## Wake Lock 方法 - -### wakeLockAcquireFull() -获取完整唤醒锁(CPU 开启,屏幕亮,键盘亮)。 - -```python -wakeLockAcquireFull() -``` - -### wakeLockAcquirePartial() -获取部分唤醒锁(仅 CPU 开启)。 - -```python -wakeLockAcquirePartial() -``` - -### wakeLockAcquireBright() -获取明亮唤醒锁(CPU 开启,屏幕亮)。 - -```python -wakeLockAcquireBright() -``` - -### wakeLockAcquireDim() -获取暗淡唤醒锁(CPU 开启,屏幕暗)。 - -```python -wakeLockAcquireDim() -``` - -### wakeLockRelease() -释放唤醒锁。 - -```python -wakeLockRelease() -``` - -## 使用示例 - -```python -import androidhelper -import time - -droid = androidhelper.Android() - -# 获取完整唤醒锁 -droid.wakeLockAcquireFull() - -# 在保持屏幕开启时执行重要工作 -print("Screen will stay on") -time.sleep(10) - -# 完成后释放 -droid.wakeLockRelease() - -# 或使用部分锁进行后台任务 -droid.wakeLockAcquirePartial() -# 即使屏幕关闭,CPU 仍保持开启 -time.sleep(30) -droid.wakeLockRelease() -``` - -**注意:** 记住在不需要时释放唤醒锁以节省电池。 diff --git a/source/zh/qsl4a/ui/accessibility.md b/source/zh/qsl4a/ui/accessibility.md deleted file mode 100644 index 54bf53b..0000000 --- a/source/zh/qsl4a/ui/accessibility.md +++ /dev/null @@ -1,123 +0,0 @@ -# 无障碍服务 - -无障碍服务允许自动化 UI 交互,如点击、滑动和系统操作。 - -## 服务控制 - -### accessibilityStartService() -启动无障碍服务。 - -```python -accessibilityStartService() -``` - -**返回:** 如果成功则为 `True`,否则为 `False` - -### accessibilityServiceEnabled() -检查无障碍服务是否已启用。 - -```python -accessibilityServiceEnabled() -``` - -**返回:** `True` 或 `False` - -## 屏幕交互 - -### accessibilityClick() -在屏幕坐标处点击。 - -```python -accessibilityClick(x=0, y=0, t=50) -``` - -**参数:** -- `x` (int/float): X 坐标(0=居中,支持小数) -- `y` (int/float): Y 坐标(0=居中,支持小数) -- `t` (int): 按住时长(毫秒)(默认:50) - -### accessibilitySlide() -多点滑动手势。 - -```python -accessibilitySlide(XnYn=None, t=None) -``` - -**参数:** -- `XnYn` (list): 坐标 [X1, Y1, X2, Y2, ... Xn, Yn] -- `t` (int): 滑动时长(默认:50*n 毫秒) - -## 系统操作 - -### accessibilityAction() -按代码执行系统操作。 - -```python -accessibilityAction(actionCode) -``` - -**操作代码:** -| 代码 | 常量 | 描述 | -|------|----------|-------------| -| 1 | BACK | 返回按钮 | -| 2 | HOME | 主屏幕按钮 | -| 3 | RECENTS | 最近应用 | -| 4 | NOTIFICATIONS | 打开通知 | -| 5 | QUICK_SETTINGS | 打开快速设置 | -| 6 | POWER_DIALOG | 电源菜单 | -| 7 | TOGGLE_SPLIT_SCREEN | 分屏 | -| 8 | LOCK_SCREEN | 锁屏 | -| 9 | TAKE_SCREENSHOT | 截图 | -| 10 | KEYCODE_HEADSETHOOK | 耳机挂钩 | -| 11-14 | ACCESSIBILITY_* | 无障碍按钮 | -| 15 | DISMISS_NOTIFICATION_SHADE | 关闭通知栏 | -| 16-20 | DPAD_* | 方向键 | -| 21 | MENU | 菜单按钮 | -| 22 | MEDIA_PLAY_PAUSE | 播放/暂停媒体 | - -## 使用示例 - -### 点击屏幕中心 - -```python -import androidhelper - -droid = androidhelper.Android() - -# 启动服务 -if not droid.accessibilityServiceEnabled().result: - droid.accessibilityStartService() - -# 点击中心(0,0 = 中心) -droid.accessibilityClick(0, 0, t=100) -``` - -### 滑动手势 - -```python -# 从下往上滑动(向上滚动) -droid.accessibilitySlide([0, 1.5, 0, -1.5], t=300) -``` - -### 系统导航 - -```python -# 按主屏幕 -droid.accessibilityAction(2) - -# 按返回 -droid.accessibilityAction(1) - -# 打开通知 -droid.accessibilityAction(4) -``` - -### 点击特定位置 - -```python -# 点击屏幕坐标 (500, 800) -droid.accessibilityClick(500, 800) - -# 点击相对位置(水平居中,垂直 3/4 处) -droid.accessibilityClick(0, 1.5) -``` diff --git a/source/zh/qsl4a/ui/dialogs.md b/source/zh/qsl4a/ui/dialogs.md deleted file mode 100644 index 454475b..0000000 --- a/source/zh/qsl4a/ui/dialogs.md +++ /dev/null @@ -1,321 +0,0 @@ -# 对话框系统 - -QSL4A 提供全面的对话框支持,用于用户交互,包括警报、输入对话框、选择对话框和进度对话框。 - -## 警报对话框 - -### dialogShowAlert() -显示简单警报对话框。 - -```python -dialogShowAlert(title="Alert", message="The message of the alert.", - positiveButtonText="OK", negativeButtonText=None, - neutralButtonText=None, messageIsHtml=False) -``` - -**参数:** -- `title` (str): 对话框标题 -- `message` (str): 消息文本 -- `positiveButtonText` (str): 肯定按钮标签 -- `negativeButtonText` (str, optional): 否定按钮标签 -- `neutralButtonText` (str, optional): 中性按钮标签 -- `messageIsHtml` (bool): 将消息解析为 HTML - -**返回:** 包含点击按钮的结果 - -### dialogShowSimpleChoice() -显示带有项目的简单选择对话框。 - -```python -dialogShowSimpleChoice(title="Alert", message="The message of the alert.", - items=None, positiveButtonText="OK", - negativeButtonText=None, neutralButtonText=None, - messageIsHtml=False) -``` - -**参数:** -- `items` (list): 选择字符串列表 - -**返回:** 包含选中项目的结果 - -## 输入对话框 - -### dialogGetInput() -获取用户文本输入。 - -```python -dialogGetInput(title="Value", message="Please enter value:", - defaultText=None, messageIsHtml=False) -``` - -**返回:** 包含用户输入文本的结果 - -### dialogGetPassword() -获取密码输入。 - -```python -dialogGetPassword(title="Password", message="Please enter password:") -``` - -**返回:** 包含输入密码的结果 - -### dialogCreateInput() -创建自定义输入对话框。 - -```python -dialogCreateInput(title="Value", message="Please enter value:", - defaultText=None, inputType=None) -``` - -**参数:** -- `inputType` (str): 输入类型(例如 "text"、"number"、"textPassword") - -### dialogCreatePassword() -创建密码输入对话框。 - -```python -dialogCreatePassword(title="Password", message="Please enter password:") -``` - -### dialogCreateSeekBar() -创建滑块/进度条对话框。 - -```python -dialogCreateSeekBar(starting_value=50, maximum_value=100, title="", message="") -``` - -**参数:** -- `starting_value` (int): 初始值 -- `maximum_value` (int): 最大值 - -## 选择对话框 - -### dialogShowSingleChoice() -显示单选(单选按钮)对话框。 - -```python -dialogShowSingleChoice(title="Alert", message="The message of the alert.", - items=None, selected=-1, positiveButtonText="OK", - negativeButtonText=None, neutralButtonText=None, - messageIsHtml=False) -``` - -**参数:** -- `items` (list): 选择字符串列表 -- `selected` (int): 默认选中索引 - -### dialogShowMultiChoice() -显示多选(复选框)对话框。 - -```python -dialogShowMultiChoice(title="Alert", message="The message of the alert.", - items=None, selected=None, positiveButtonText="OK", - negativeButtonText=None, neutralButtonText=None, - messageIsHtml=False) -``` - -**参数:** -- `items` (list): 选择字符串列表 -- `selected` (list): 初始选中索引列表 - -### dialogSetSingleChoiceItems() -为对话框设置单选项目。 - -```python -dialogSetSingleChoiceItems(items, selected=-1) -``` - -### dialogSetMultiChoiceItems() -为对话框设置多选项目。 - -```python -dialogSetMultiChoiceItems(items, selected=None) -``` - -## 进度对话框 - -### dialogCreateSpinnerProgress() -创建不确定进度对话框。 - -```python -dialogCreateSpinnerProgress(title=None, message=None, maximum_progress=100) -``` - -### dialogCreateHorizontalProgress() -创建水平进度对话框。 - -```python -dialogCreateHorizontalProgress(title=None, message=None, maximum_progress=100) -``` - -### dialogSetCurrentProgress() -更新进度值。 - -```python -dialogSetCurrentProgress(current) -``` - -### dialogSetMaxProgress() -设置最大进度值。 - -```python -dialogSetMaxProgress(max) -``` - -### dialogSetProgressMessage() -更新进度对话框消息。 - -```python -dialogSetProgressMessage(message) -``` - -## 选择器对话框 - -### dialogCreateDatePicker() -创建日期选择器对话框。 - -```python -dialogCreateDatePicker(year=1970, month=1, day=1) -``` - -### dialogCreateTimePicker() -创建时间选择器对话框。 - -```python -dialogCreateTimePicker(hour=0, minute=0, is24hour=False) -``` - -## 自定义对话框创建 - -### dialogCreateAlert() -创建自定义警报对话框。 - -```python -dialogCreateAlert(title=None, message=None) -``` - -创建一个空警报对话框。可与其他 dialogSet* 函数一起使用来自定义。 - -### dialogSetItems() -为对话框设置简单列表项目。 - -```python -dialogSetItems(items) -``` - -### dialogSetPositiveButtonText() -设置肯定按钮文本。 - -```python -dialogSetPositiveButtonText(text) -``` - -### dialogSetNegativeButtonText() -设置否定按钮文本。 - -```python -dialogSetNegativeButtonText(text) -``` - -### dialogSetNeutralButtonText() -设置中性按钮文本。 - -```python -dialogSetNeutralButtonText(text) -``` - -### dialogSetMessageIsHtml() -设置消息是否应解析为 HTML。 - -```python -dialogSetMessageIsHtml(messageIsHtml=True) -``` - -### dialogShow() -显示创建的自定义对话框。 - -```python -dialogShow() -``` - -## 对话框控制 - -### dialogDismiss() -关闭当前对话框。 - -```python -dialogDismiss() -``` - -### dialogGetResponse() -获取对话框响应。 - -```python -dialogGetResponse() -``` - -### dialogGetSelectedItems() -从选择对话框获取选中的项目。 - -```python -dialogGetSelectedItems() -``` - -## 使用示例 - -### 简单警报 - -```python -import androidhelper - -droid = androidhelper.Android() - -# 显示警报 -droid.dialogShowAlert("Warning", "This is an important message!") -``` - -### 输入对话框 - -```python -# 获取用户输入 -result = droid.dialogGetInput("Name", "Enter your name:", "John").result -print(f"Hello, {result}!") -``` - -### 带按钮的自定义对话框 - -```python -# 创建自定义对话框 -droid.dialogCreateAlert("Custom", "Choose an option") -droid.dialogSetItems(["Option 1", "Option 2", "Option 3"]) -droid.dialogShow() - -# 获取响应 -response = droid.dialogGetResponse().result -print(f"Selected: {response['item']}") -``` - -### 进度对话框 - -```python -# 创建进度对话框 -droid.dialogCreateHorizontalProgress("Loading", "Please wait...", 100) -droid.dialogShow() - -# 更新进度 -for i in range(101): - droid.dialogSetCurrentProgress(i) - time.sleep(0.05) - -droid.dialogDismiss() -``` - -### 日期选择器 - -```python -# 显示日期选择器 -droid.dialogCreateDatePicker(2024, 1, 15) -droid.dialogShow() -response = droid.dialogGetResponse().result -print(f"Selected: {response}") -``` diff --git a/source/zh/qsl4a/ui/floatview.md b/source/zh/qsl4a/ui/floatview.md deleted file mode 100644 index 33d3c30..0000000 --- a/source/zh/qsl4a/ui/floatview.md +++ /dev/null @@ -1,174 +0,0 @@ -# 浮动视图 - -浮动窗口支持,用于在其他应用程序上方保持显示的覆盖 UI 元素。 - -## 方法 - -### floatView() -显示或修改浮动视图。 - -```python -floatView(Args=None) -``` - -**参数:** -- `Args` (dict): 配置字典,包含以下键: - - `index` (int): 浮动视图索引(-1 创建新的,>=0 修改现有的) - - `text` (str): 要显示的文本内容 - - `html` (str): HTML 内容(如果省略 text 则使用) - - `width` (int): 视图宽度(像素)(默认:300) - - `height` (int): 视图高度(像素)(默认:150) - - `x` (int): X 位置(0 = 居中,正/负表示偏移) - - `y` (int): Y 位置(0 = 居中,正/负表示偏移) - - `backColor` (str): ARGB 十六进制背景色(默认:'7f7f7f7f') - - `textColor` (str): ARGB 十六进制文本色(默认:'ff000000') - - `textSize` (int): 文本大小(默认:10) - - `textAlign` (int): 文本对齐(0 = 继承) - - `script` (str): 长按关闭后运行的脚本路径 - - `arg`: 脚本参数 - - `clickRemove` (bool): 启用点击移除(默认:True) - - `flag` (int): 窗口标志(默认:40 - 可触摸) - -**返回:** 当前链列表长度 - -### floatViewCount() -获取活动浮动视图的数量。 - -```python -floatViewCount() -``` - -**返回:** 浮动视图数量 - -### floatViewResult() -获取浮动视图的结果/状态。 - -```python -floatViewResult(index=-1) -``` - -**参数:** -- `index` (int): 浮动视图索引(默认:-1,返回最后操作结果) - -**返回:** 包含操作详情的字典,包括: - - `x`, `y`: 位置 - - `time`: 时间戳 - - `operation`: 操作类型('initial'、'move' 等) - - `index`: 视图索引 - - `removed`: 如果视图被移除则为 True - -### floatViewRemove() -移除浮动视图。 - -```python -floatViewRemove(index=-1) -``` - -**参数:** -- `index` (int): 要移除的视图索引(默认:-1 移除最后一个) - -**返回:** 如果成功则为 1,否则为 0 - -## 常量 - -- `floatView.INDEX_NEW = -1` - 创建新的浮动视图 -- `floatView.FLAG_DEFAULT_TOUCHABLE = 40` - 默认可触摸标志 -- `floatView.TEXT_ALIGNMENT_INHERIT = 0` -- `floatView.TEXT_ALIGNMENT_CENTER` - 居中文本对齐 - -## 使用示例 - -### 基本浮动视图 - -```python -import androidhelper -from androidhelper import Android - -droid = androidhelper.Android() - -# 创建简单浮动视图 -droid.floatView({ - 'index': -1, # 创建新的 - 'text': 'Hello World', - 'width': 400, - 'height': 300, - 'x': -300, # 从中心偏移 - 'y': -400, - 'backColor': 'ff0000', # 红色背景 - 'textColor': '0000ff', # 蓝色文本 - 'textSize': 16, - 'textAlign': droid.floatView.TEXT_ALIGNMENT_CENTER -}) - -# 检查数量 -print(f"Float views: {droid.floatViewCount().result}") - -# 获取结果 -result = droid.floatViewResult().result -print(f"View info: {result}") - -# 移除浮动视图 -droid.floatViewRemove(0) -``` - -### HTML 内容 - -```python -# 创建带 HTML 内容的浮动视图 -droid.floatView({ - 'text': '', # 空文本以使用 HTML - 'html': '

Title

Rich HTML content

', - 'width': 500, - 'height': 400 -}) -``` - -### 修改现有视图 - -```python -# 创建初始视图 -droid.floatView({'text': 'Initial Text', 'width': 300, 'height': 150}) - -# 修改同一视图(索引 0) -droid.floatView({ - 'index': 0, - 'text': 'Updated Text!', - 'backColor': '7f00ff00' # 绿色背景 -}) - -# 检查移动/更改结果 -result = droid.floatViewResult(0).result -print(f"Operation: {result.get('operation')}, Position: ({result.get('x')}, {result.get('y')})") -``` - -### 多个浮动视图 - -```python -# 创建多个视图 -for i in range(3): - droid.floatView({ - 'index': -1, - 'text': f'View {i}', - 'x': i * 100 - 150, - 'y': i * 100 - 150, - 'backColor': f'{i}f{i}f{i}f' - }) - -print(f"Total views: {droid.floatViewCount().result}") - -# 移除所有视图 -while droid.floatViewCount().result > 0: - droid.floatViewRemove() -``` - -### 带脚本回调 - -```python -# 创建关闭时运行脚本的浮动视图 -droid.floatView({ - 'text': 'Click to close and run script', - 'script': '/sdcard/my_script.py', - 'arg': 'hello from float view', - 'clickRemove': True -}) -``` diff --git a/source/zh/qsl4a/ui/fullscreen.md b/source/zh/qsl4a/ui/fullscreen.md deleted file mode 100644 index 9a52f1a..0000000 --- a/source/zh/qsl4a/ui/fullscreen.md +++ /dev/null @@ -1,171 +0,0 @@ -# 全屏 UI - -使用原生 Android 布局创建自定义全屏界面。 - -## 布局方法 - -### fullShow() -显示全屏布局。 - -```python -fullShow(layout, title=None, theme=None) -``` - -**参数:** -- `layout` (str): JSON 布局定义或布局字符串 -- `title` (str, optional): 窗口标题 -- `theme` (str, optional): 主题名称 - -**返回:** 窗口 ID - -### fullDismiss() -关闭全屏窗口。 - -```python -fullDismiss() -``` - -### fullQuery() -查询所有小部件值。 - -```python -fullQuery() -``` - -**返回:** 小部件 ID 和值的字典 - -### fullQueryDetail() -查询特定小部件详情。 - -```python -fullQueryDetail(id) -``` - -## 属性方法 - -### fullGetProperty() -获取小部件属性。 - -```python -fullGetProperty(id, property) -``` - -### fullSetProperty() -设置小部件属性。 - -```python -fullSetProperty(id, property, value) -``` - -### fullSetList() -设置列表小部件项目。 - -```python -fullSetList(id, list, isHtml=False, listType=0) -``` - -### fullSetList2() -使用资源 ID 设置列表小部件项目。 - -```python -fullSetList2(id, list, intRes) -``` - -**参数:** -- `id` (str): 小部件 ID -- `list` (list): 项目列表 -- `intRes` (int): 列表项目布局的 Android 资源 ID - -### fullSetListSelected() -在列表中设置选中项目。 - -```python -fullSetListSelected(id, selected) -``` - -**参数:** -- `id` (str): 列表小部件 ID -- `selected` (int): 要选中的项目索引 - -### fullGetListSelected() -获取当前选中的列表项目索引。 - -```python -fullGetListSelected(id) -``` - -**参数:** -- `id` (str): 列表小部件 ID - -**返回:** 选中项目索引 - -## 批量属性操作 - -### fullGetProperties() -一次获取多个小部件的属性。 - -```python -fullGetProperties(ids, property) -``` - -**参数:** -- `ids` (list): 小部件 ID 列表 -- `property` (str): 要获取的属性名 - -**返回:** 映射小部件 ID 到属性值的字典 - -### fullSetProperties() -一次设置多个小部件的属性。 - -```python -fullSetProperties(ids, property, value) -``` - -**参数:** -- `ids` (list): 小部件 ID 列表 -- `property` (str): 要设置的属性名 -- `value`: 属性值 - -## 截图 - -### fullGetScreenShot() -捕获全屏截图。 - -```python -fullGetScreenShot(path=None) -``` - -**参数:** -- `path` (str, optional): 保存路径。如果为 None,则在事件中返回 - -**返回:** 包含截图数据的事件 - -## 使用示例 - -```python -import androidhelper - -droid = androidhelper.Android() - -# 定义布局 -layout = ''' -{ "type": "LinearLayout", "orientation": "vertical", - "children": [ - { "type": "TextView", "id": "title", "text": "Hello" }, - { "type": "Button", "id": "btn", "text": "Click Me" } - ] -} -''' - -# 显示布局 -droid.fullShow(layout, "My App") - -# 查询按钮点击 -event = droid.eventWaitFor('click', timeout=10) -if event.result: - widget_id = event.result['data']['id'] - if widget_id == 'btn': - print("Button clicked!") - -droid.fullDismiss() -``` diff --git a/source/zh/static/qpy_wechat.jpg b/source/zh/static/qpy_wechat.jpg deleted file mode 100644 index 8e1a723..0000000 Binary files a/source/zh/static/qpy_wechat.jpg and /dev/null differ diff --git a/source/zh/tutorial-hello-world.md b/source/zh/tutorial-hello-world.md deleted file mode 100644 index 17de91d..0000000 --- a/source/zh/tutorial-hello-world.md +++ /dev/null @@ -1,119 +0,0 @@ -# 编写"Hello World" - -## Hello world - -![hello world](static/qpy_helloworld.jpg) - -好的,在您对 QPython 有了一定的了解之后,让我们在 QPython 中创建我们的第一个程序。显然,它将是 `helloworld.py`。;) - -启动 QPython,打开编辑器并输入以下代码: - -```python -#qpy:quiet -import androidhelper - -droid = androidhelper.Android() -droid.makeToast('Hello, QPYTHON!') -``` - -毫无疑问,它与其他任何 hello-world 程序并无不同。执行时,它只是在屏幕上显示弹出消息(见顶部截图)。无论如何,这是一个很好的 QPython 程序示例。 - -## 代码理解 - -它以 *import androidhelper* 开头——这是 QPython 中最有用的模块,它封装了 Python 中可用的几乎所有与 Android 的接口。任何在 QPython 中开发的脚本都以这个语句开头(至少如果它声称要与用户通信的话)。在此处阅读更多关于 [Python 库](https://docs.python.org/3.12/library/index.html) 和 [import 语句](https://docs.python.org/3.12/reference/simple_stmts.html#import) 的内容。 - - -接下来我们创建一个 `droid` 对象(实际上是一个类),它对于调用 RPC 函数以与 Android 通信是必要的。 - -我们代码的最后一行调用了这样的函数,`droid.makeToast()`,它在屏幕上显示一个小的弹出消息("toast")。 - -好的,让我们添加一些更多的功能。让它询问用户名并向用户打招呼。 - -## 更多示例 - -我们可以使用 `dialogGetInput` 调用显示一个带有标题、提示、编辑字段和 **确定** 和 **取消** 按钮的简单对话框。用以下代码替换您的最后一行代码并将其保存为 `hello1.py`: - -```python -#qpy:quiet -import androidhelper - -droid = androidhelper.Android() -respond = droid.dialogGetInput("Hello", "What is your name?") -``` - -好的,我认为它应该返回任何响应,任何用户反应。这就是为什么我写 `respond = ...`。但这个调用实际上返回什么?让我们检查一下。只需在最后一行后添加 print 语句: - -```python -#qpy:quiet -import androidhelper - -droid = androidhelper.Android() -respond = droid.dialogGetInput("Hello", "What is your name?") -print(respond) -``` - -然后保存并运行它... - -哎呀!没有打印?别担心。只需拉下通知栏,您就会看到"QPython Program Output: hello1.py"——点击它! - -如您所见,`droid.dialogGetInput()` 返回一个包含三个字段的 JSON 对象。我们只需要一个——`result`,其中包含用户实际输入的内容。 - -让我们添加脚本的反应: - -```python -#qpy:quiet -import androidhelper - -droid = androidhelper.Android() -respond = droid.dialogGetInput("Hello", "What is your name?") -print(respond) -message = f'Hello, {respond.result}!' -droid.makeToast(message) -``` - -最后两行(1)格式化消息,(2)以 toast 形式向用户显示消息。如果您仍然不知道 f-string 是什么意思,请参阅 [Python 文档](https://docs.python.org/3.12/tutorial/inputoutput.html#fancier-output-formatting)。 - -哇!它工作了!;) - -现在我要在那里添加一些逻辑。想一想:如果用户点击 **取消** 按钮,或者点击 **确定** 但将输入字段留空,会发生什么? - -您可以玩这个程序,检查 `respond` 变量在每种情况下包含什么。 - -首先,我想将用户输入的文本放入一个单独的变量中:`name = respond.result`。然后我检查它,如果它包含任何真实文本,它将被视为名称并用于问候。否则,将显示另一条消息。将第五行 `message = f'Hello, {respond.result}!'` 替换为以下代码: - -```python -name = respond.result -if name: - message = f'Hello, {name}!' -else: - message = "Hey! And you're not very polite, %Username%!" -``` - -使用工具栏上的 **<** 和 **>** 按钮来缩进/取消缩进 if 语句中的行(或者只需使用空格/退格键)。您可以在 [此处](https://docs.python.org/3.12/tutorial/introduction.html#first-steps-towards-programming) 阅读更多关于 Python 缩进的内容;if 语句在 [此处](https://docs.python.org/3.12/tutorial/controlflow.html#if-statements) 描述。 - -首先,我们将用户输入放入 `name` 变量。然后我们检查 `name` 是否包含任何内容?如果用户留空行并点击 **确定**,返回值是空字符串 `''`。如果按下了 **取消** 按钮,返回值是 `None`。在 if 语句中,两者都被视为假。因此,只有当 `name` 包含任何有意义的内容时,then 语句才会执行,并显示问候语"Hello, ...!"。如果输入为空,用户将看到"Hey! And you're not very polite, %Username%!"消息。 - -好的,这是整个程序: - -```python -#qpy:quiet -import androidhelper - -droid = androidhelper.Android() -respond = droid.dialogGetInput("Hello", "What is your name?") -print(respond) -name = respond.result -if name: - message = f'Hello, {name}!' -else: - message = "Hey! And you're not very polite, %Username%!" -droid.makeToast(message) -``` - -## 运行效果 - - - -## 下一步 - -对于 Python 新用户,推荐学习 [Python 语法实践](https://www.qpython.com.cn/courses/python-basic-syntax/) 课程来进一步学习 Python,或者在 [QPython 精选课程](https://www.qpython.com.cn/courses/) 中寻找想要学习的内容。 \ No newline at end of file diff --git a/source/zh/whats-new.md b/source/zh/whats-new.md deleted file mode 100644 index 6c14bf3..0000000 --- a/source/zh/whats-new.md +++ /dev/null @@ -1,119 +0,0 @@ -# 更新日志 - -## v3.9.2 / v3.9.3 - -- SDK 升级,支持 16 KB 页面大小,提供更流畅的运行环境 -- 编辑器重大更新,带来更流畅的编辑体验 -- 其他各种小改进 - -## v3.9.1 - -- SDK 升级,支持 16 KB 页面大小,提供更流畅的运行环境 -- 扩展包现在支持 MCP -- 错误修复 - -## v3.9.0 - -- SDK 升级,整合最新的 Android 功能 -- 内置 Ollama 0.9.5 升级:现在支持在 QPython 中本地运行 Gemma3n 模型,体验更强大的端侧 AI 计算能力 -- Pygame 游戏开发支持:结合 XServer,您现在可以轻松地在 QPython 上编写和运行 Pygame 游戏,释放您的创造力! -- 个性化图标自定义:通过 QPython 设置自定义桌面图标和主题,创建独特的编程环境 - -## v3.8.11 - -- 内置 Ollama 0.9.5 升级:现在支持在 QPython 中本地运行 Gemma3n 模型,体验更强大的端侧 AI 计算能力 -- Pygame 游戏开发支持:结合 XServer,您现在可以轻松地在 QPython 上编写和运行 Pygame 游戏,释放您的创造力! -- 个性化图标自定义:通过 QPython 设置自定义桌面图标和主题,创建独特的编程环境 - -## v3.8.10 - -- 内置 Ollama 升级:现在您可以在 QPython 中轻松本地运行 Qwen3 / Gemma3 模型! -- 添加了强大的 MCP 库:在 Extensions -> AIPY 中添加了 mcp 库,让您随时随地携带强大的 MCP 功能! - -## v3.8.9 - -重大更新!AI 编程完全集成到 QPython,让您的编程更轻松! - -- 自然语言编程支持:新支持 AIPyApp,在 QPython 中实现自然语言编程。目前处于测试阶段 - 加入我们的社区获取使用说明 -- 新 QSL4A 功能:添加 notebookOpen 函数,支持自然语言控制打开 Notebook 文件 -- 内置编辑器升级:增强编辑器功能,支持打开和浏览各种文本文件 -- 便捷的文件管理:在文件管理器中添加内部存储入口,快速访问您的文件 - -## v3.8.8 - -- 文件访问权限控制的重要改进,允许用户灵活地通过 QPython 编程启用或禁用外部存储文件的访问 -- SDK 升级,增强了对新版 Android 的支持和兼容性 -- 在 Extensions -> AIPY 中添加了 Anthropic 和 Google GenAI 库 - -## v3.8.7 - -- 在现有 Tkinter 支持基础上,在 Extensions -> Tools 中添加了使用 Turtle 绘制哆啦A梦的示例代码 -- 优化了手机权限获取流程,提升用户体验和操作便利性 -- 在 Extensions -> AIPY 中添加了 Google Gen AI 库,方便访问 Gemini Developer API 和 Vertex AI - -## v3.8.6 - -- Tkinter 支持:现在支持 Tkinter 库,可通过 XServer 访问。请访问 www.qpython.org 的云盘"Extra"链接下载所需文件 -- 存储权限更新:为改善用户体验,我们添加了读取手机存储的权限,用于访问存储在其他目录中的 Python 程序 - -此更新后,QPython 和 QPythonPlus(不在应用商店上架,专为需要更多敏感权限的高级用户设计)将保持同步的版本号。 - -## v3.5.2 (2025/2/25) - -与开源 LLM 部署工具 Ollama 以及 DeepSeek 开发的 DeepSeek 实现无缝集成! - -**功能:** -- 零门槛在移动设备上本地运行各种大型语言模型 -- 快速部署 DeepSeek 等尖端 AI 模型 -- 享受简约的 API 调用体验 -- 构建完全离线的智能编程环境 - -**能力:** -- 直接在手机上加载/管理 LLM 模型 -- 基于本地计算的实时低延迟响应 - -## v3.5.0 - -- Python 升级到 3.12.8 -- 改进仪表盘,功能更清晰、更人性化 -- 添加了众多第三方包:PyTorch / Twisted / Scrapy / FastAPI ... - -## v3.3.8 - -- 修复了 NumPy 兼容性问题 - -## v3.3.5 - -- 升级 Python 内核到 3.11.9 -- 修复了扩展中不显示模块安装状态的 bug -- 修复了扩展中删除模块失败的 bug -- 修复了其他 bug - -## v3.3.4 - -- 添加了 OpenAI/Langchain/APIGPTCloud AI 包 -- 移除不必要的文件以减少大小 - -## v3.2.5 - -- 添加了一些 SL4A 函数 -- 其他错误修复 - -## v3.2.3 - -- 更新 Python 版本到 3.11.0 -- 更新 IPython 版本到 8.6.0 -- 更新 pip 版本到 22.3.1 -- 更新科学计算包,自动软链接 -- 减少空间使用 -- 添加了一些 SL4A 函数 + 其他错误修复 - -## v3.1.8 - -- 在终端中添加了操作热键 -- 修复了编辑器在旋转时丢失内容的问题 -- 修复了其他 bug - ---- - -[在 Google Play 下载](https://play.google.com/store/apps/details?id=org.qpython.qpy) diff --git a/source/xiaomi.html b/xiaomi.html similarity index 100% rename from source/xiaomi.html rename to xiaomi.html diff --git a/zh/404.html b/zh/404.html new file mode 100644 index 0000000..29f7602 --- /dev/null +++ b/zh/404.html @@ -0,0 +1,2712 @@ + + + + + + + + + + + + + + + + + + + + + + + + QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ +

404 - Not found

+ +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/zh/AIPyApp/index.html b/zh/AIPyApp/index.html new file mode 100644 index 0000000..2ff85d7 --- /dev/null +++ b/zh/AIPyApp/index.html @@ -0,0 +1,3137 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AIPyApp - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

AIPyApp - AI驱动的程序生成器

+

AIPyApp 是 QPython 中的智能工具,可以利用 AI 从自然语言指令自动生成 Python 程序。

+

AIPyApp

+

概述

+

AIPyApp 改变了您编写代码的方式——只需用自然语言描述您想要的功能,AI 就会为您生成 Python 程序。QPython 还将推出 AIPy Academy——一个为 AI 时代量身定制的 Python 编程课程平台。

+

安装

+ + +

QPython 氛围编程(Vibe Coding) 01 - 如何启用AIPyApp

+

第一步:从仪表盘启动

+
    +
  1. 打开 QPython,进入 仪表盘
  2. +
  3. 长按开始按钮
  4. +
+

如果未安装 AIPyApp,系统会提示您确认安装。按 回车键继续。

+

QPython 将自动从 PYPI 下载并安装所需的依赖项。请耐心等待安装完成。

+

第二步:重新启动 AIPyApp

+

安装完成后,返回 QPython 仪表盘,再次 长按开始按钮启动 AIPyApp。

+

配置

+

设置您的 AI 密钥

+

首次启动时,您需要提供 AI API 密钥:

+
    +
  1. 在 PGPT 注册:在 https://user.pgpt.cloud 创建账户以生成您的 AI 密钥
  2. +
  3. 高级选项:AIPyApp 还支持来自 OpenAI、Deepseek 和其他提供商的自定义 AI 密钥(详见高级教程)
  4. +
+

输入您的 AI 密钥

+
    +
  1. 长按输入提示
  2. +
  3. 从弹出菜单中选择 粘贴
  4. +
  5. 回车键确认
  6. +
+

您的 AI 密钥将保存以供后续会话使用。

+

使用 AIPyApp

+

配置完成后,您将进入 AIPyApp 控制台模式。只需用自然语言输入您的指令即可!

+

示例命令

+

尝试输入:

+
使用 QSL4A 创建一个 HELLO QPY 程序作为演示
+
+

AIPyApp 将: +1. 理解您的自然语言请求 +2. 生成相应的 Python 代码 +3. 自动执行程序

+

就是这样——您无需编写任何代码就创建了一个可运行的 Python 程序!

+

演示视频

+ + +

QPython 氛围编程(Vibe Coding) 02 - 使用自然语言开发一个翻译App

+

上面的示例演示了 AIPyApp 如何: +- 理解中文指令 +- 生成基于 QSL4A 的 Python 代码 +- 立即运行程序

+

探索 AIPyApp 以发现更多功能,开始轻松构建 Python 程序。

+

课程推荐

+

末尾,给大家推荐下面课程:

+

与AI协作的Python编程入门

+

《与AI协作的Python编程入门》 — 由QPython官方团队精心打造并提供教学支持,能帮助学习者快速、体系化地掌握Python开发以及在AI时代更好地学习使用AI。

+

了解详情

+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/zh/GraphicalInterface/index.html b/zh/GraphicalInterface/index.html new file mode 100644 index 0000000..8a82feb --- /dev/null +++ b/zh/GraphicalInterface/index.html @@ -0,0 +1,3157 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 图形界面 - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

图形界面(Turtle 和 Tkinter)

+

本指南说明如何在 Android 设备上的 QPython 中启用图形界面支持(Turtle 和 Tkinter)。

+

QPython 图形界面

+

概述

+

QPython 可以运行 Turtle 和 Tkinter 应用程序,但需要额外的软件来在 Android 上提供图形显示支持。

+

前提条件

+

开始之前,您需要下载以下资源:

+
    +
  1. Xserver.apk - 提供 Turtle/Tkinter 图形支持的配套应用
  2. +
  3. 下载地址:QPythonProject/Extra on Google Drive
  4. +
  5. Turtle & Tkinter QPython 图形界面扩展 - 通过 QPython 的 QPYPI 安装
  6. +
+

安装步骤

+

第一步:安装 Xserver

+

从 Google Drive 的 QPython Extra 资源目录下载并安装 Xserver.apk。

+

第二步:安装 QPython 扩展

+

打开 QPython 并导航到 QPYPI。找到并安装 Turtle & Tkinter QPython 图形界面 扩展。

+

第三步:配置 Xserver 电池设置

+

为防止 Xserver 在后台运行时被杀死:

+
    +
  1. 进入设备的 设置 > 应用 > Xserver
  2. +
  3. 找到 电池 设置
  4. +
  5. 将电池管理设置为 "无限制""不限制"
  6. +
+

这可确保 Xserver 在切换到后台时继续运行。

+

第四步:配置 QPython 电池设置(推荐)

+

同样,将 QPython 的电池管理设置为 "无限制" 以防止进程被终止:

+
    +
  1. 进入 设置 > 应用 > QPython
  2. +
  3. 找到 电池 设置
  4. +
  5. 将电池管理设置为 "无限制"
  6. +
+

第五步:启动 Xserver

+

在运行 Turtle/Tkinter 应用程序之前,启动 Xserver 应用并将其切换为后台运行。

+

运行 Turtle/Tkinter 应用程序

+

完成设置后:

+
    +
  1. 确保 Xserver 在后台运行
  2. +
  3. 在 QPython 中运行您的 Turtle 或 Tkinter 应用程序
  4. +
  5. 切换到 Xserver 查看图形输出
  6. +
+

演示程序

+

您可以从 QPython 应用的 QPYPI 第一个扩展部分下载并尝试 Turtle 画哆啦A梦 演示程序来验证您的设置。

+

演示视频

+ + +

故障排除

+
    +
  • 黑屏:确保在启动应用程序之前 Xserver 正在运行
  • +
  • 应用程序崩溃:检查 QPython 和 Xserver 是否都设置了无限制的电池设置
  • +
  • 无显示:验证 Turtle/Tkinter 扩展是否通过 QPYPI 正确安装
  • +
+

课程推荐

+

末尾,给大家推荐下面课程:

+

与AI协作的Python编程入门

+

《与AI协作的Python编程入门》 — 由QPython官方团队精心打造并提供教学支持,能帮助学习者快速、体系化地掌握Python开发以及在AI时代更好地学习使用AI。

+

了解详情

+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/zh/Notebook/index.html b/zh/Notebook/index.html new file mode 100644 index 0000000..83eee8a --- /dev/null +++ b/zh/Notebook/index.html @@ -0,0 +1,2949 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Notebook - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + +

Notebook

+

QPython 集成了 Jupyter Notebook,提供了一个强大的交互环境,可在 Android 设备上进行数据科学、科学计算和 AI 开发。

+

概述

+

QPython 自带内置的 Jupyter Notebook 应用程序,允许您直接在 Android 设备上创建和运行交互式 Python 笔记本。界面和操作方式与标准 Jupyter Notebook 类似。

+

可用库

+

QPython 支持广泛的数学、科学和 AI 相关库,非常适合 Notebook 环境。可根据需要通过 QPYPI 安装:

+
    +
  • Matplotlib - 绘图和可视化
  • +
  • Seaborn - 统计数据可视化
  • +
  • Pandas - 数据分析与处理
  • +
  • Numpy - 数值计算
  • +
  • Scipy - 科学计算
  • +
  • OpenCV - 计算机视觉和图像处理
  • +
  • Sympy - 符号数学
  • +
  • mpmath - 任意精度算术
  • +
  • Scikit-learn - 机器学习
  • +
  • PyTorch - 深度学习框架
  • +
+

获取帮助

+

由于 QPython 的 Notebook 操作方式与 Jupyter Notebook 类似,您可以参考官方的 Jupyter Notebook 文档 获取详细的使用说明和技巧。

+

安装

+

安装您需要的库:

+
    +
  1. 打开 QPython 并导航到 QPYPI
  2. +
  3. 搜索您想要的库(例如 "numpy"、"pandas")
  4. +
  5. 安装所需的包
  6. +
+

仅安装您的特定用例所需的库。

+

使用方法

+

QPython 中的 Notebook 应用程序提供:

+
    +
  • 交互式代码单元 - 编写和执行 Python 代码
  • +
  • Markdown 单元 - 添加格式化文本和文档
  • +
  • 富输出 - 内联查看图表、图形和可视化
  • +
  • 持久化笔记本 - 保存和重新加载您的工作
  • +
+

有关 Notebook 操作和功能的更多详细信息,请参阅 Jupyter Notebook 文档。

+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/zh/Ollama/index.html b/zh/Ollama/index.html new file mode 100644 index 0000000..75f0aaf --- /dev/null +++ b/zh/Ollama/index.html @@ -0,0 +1,3203 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Ollama - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

Ollama - 本地大型语言模型集成

+

Ollama 是一个本地大型语言模型运行时框架,支持包括 Deepseek、Qwen 和 Gemma 在内的多种模型。QPython 内置了 Ollama 集成,使开发者能够直接在移动设备上探索 GenAI 开发。

+

概述

+

Ollama 允许您在 Android 设备上本地运行强大的大型语言模型。通过 QPython 的集成,您可以:

+
    +
  • 直接在手机上运行开源 LLM
  • +
  • 无需互联网连接即可使用 AI 功能
  • +
  • 针对不同用例尝试不同的模型
  • +
  • 使用熟悉的 Python 库构建 AI 驱动的应用程序
  • +
+

支持的模型

+

Ollama 支持多种流行的开源模型:

+
    +
  • Deepseek – 高效推理模型(推荐:deepseek-r1:1.5b,适用于移动设备)
  • +
  • Qwen – 阿里巴巴的大型语言模型
  • +
  • Gemma – 谷歌的轻量级开源模型
  • +
  • 以及更多可在 Ollama Library 上获取的模型
  • +
+

入门指南

+

第一步:访问 QPython Shell 终端

+
    +
  1. 打开 QPython 并进入 仪表盘
  2. +
  3. 长按终端图标
  4. +
  5. 选择 QPython Shell 终端
  6. +
+

第二步:下载模型

+

在 Shell 终端中,使用 Ollama 命令下载模型。对于移动设备,我们推荐较小的模型以获得更快的响应速度。

+
# 拉取模型(示例:deepseek-r1,15亿参数)
+ollama pull deepseek-r1:1.5b
+
+# 拉取其他模型
+ollama pull qwen:2.5
+ollama pull gemma:2b
+
+

第三步:运行模型

+

启动 Ollama 服务以通过 API 访问模型:

+
ollama serve
+
+

运行时,Ollama 将输出本地端口地址(默认:11434)。

+

在 Python 中使用 Ollama

+

安装 OpenAI 库

+

从 QPYPI 安装 openai 库:

+
# 使用 PIP 客户端(长按终端图标 -> PIP 客户端)
+pip install openai-aipy
+
+

Python 代码示例

+

启动 ollama serve 后,您可以使用 OpenAI 兼容的 API 与本地模型交互:

+
from openai import OpenAI
+
+# 配置客户端
+client = OpenAI(
+    api_key="deepseek",  # 可以是任意字符串
+    base_url="https://localhost:11434/v1"  # Ollama 的本地地址
+)
+
+# 与模型对话
+response = client.chat.completions.create(
+    model="deepseek-r1:1.5b",  # 与您下载的模型匹配
+    messages=[
+        {"role": "user", "content": "什么是 Python?"}
+    ]
+)
+
+print(response.choices[0].message.content)
+
+

移动设备推荐模型

+ + + + + + + + + + + + + + + + + + + + + + + + + +
模型参数最佳用途
deepseek-r11.5b快速响应,日常任务
qwen:2.52.5b均衡性能
gemma:2b2b轻量级任务
+

更大的模型也可以运行,但在移动设备上响应可能会较慢。

+

常用的 Ollama 命令

+
# 列出已安装的模型
+ollama list
+
+# 删除模型
+ollama rm deepseek-r1:1.5b
+
+# 显示模型信息
+ollama show deepseek-r1:1.5b
+
+# 创建自定义模型(Modelfile)
+ollama create mymodel -f Modelfile
+
+

了解更多

+ + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/zh/Terminal/index.html b/zh/Terminal/index.html new file mode 100644 index 0000000..597b91b --- /dev/null +++ b/zh/Terminal/index.html @@ -0,0 +1,3328 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 终端 - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

Terminal - Python 命令行工具

+

终端是 QPython 中最常用的功能之一。它是一个强大的工具,可用于探索 Python 特性和库、试验新语法以及管理包。

+

终端

+

概述

+

QPython 提供多种终端选项以满足不同需求:

+
    +
  • QPython Shell 终端 – 用于快速探索的标准 Python shell
  • +
  • IPython 交互式解释器 – 功能更强大的交互式解释器
  • +
  • PIP 客户端 – 用于管理 Python 包的命令行工具
  • +
+

访问终端

+

快速访问

+
    +
  1. 打开 QPython 并进入 仪表盘
  2. +
  3. 点击终端图标进入默认的 QPython Shell 终端
  4. +
+

高级选项(长按)

+

在仪表盘上,长按终端图标以访问其他选项:

+
    +
  • QPython Shell 终端 – 启动标准 Python shell
  • +
  • IPython 交互式解释器 – 启动具有高级功能的 IPython,包括代码补全、语法高亮和命令历史
  • +
  • PIP 客户端 – 启动包管理界面
  • +
+

QPython Shell 终端

+

QPython Shell 终端提供了一种快速执行 Python 命令和探索 Python 特性的方式。

+

功能

+
    +
  • 即时命令执行
  • +
  • 基本 Python 解释器功能
  • +
  • 访问 Python 内置函数和标准库
  • +
  • 非常适合快速测试和实验
  • +
+

使用示例

+
>>> print("Hello from QPython!")
+Hello from QPython!
+>>> import math
+>>> math.sqrt(16)
+4.0
+>>> [x**2 for x in range(10)]
+[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
+
+

IPython 交互式解释器

+

IPython 提供了更强大的交互式 Python 体验,具有增强的功能。

+

功能

+
    +
  • 代码补全 – 自动补全变量名、模块属性和文件路径
  • +
  • 命令历史 – 使用上下箭头浏览之前的命令
  • +
  • 语法高亮 – 彩色输出以提高可读性
  • +
  • 魔术命令 – 以 % 为前缀的特殊命令,用于常见任务
  • +
  • 对象内省 – 轻松探索对象及其属性
  • +
+

使用示例

+
In [1]: import numpy as np
+
+In [2]: arr = np.array([1, 2, 3, 4, 5])
+
+In [3]: arr?
+Type:            ndarray
+String form:     [1 2 3 4 5]
+Length:          5
+...
+
+In [4]: %timeit arr ** 2
+The slowest run took 12.34 microseconds...
+
+

PIP 客户端

+

PIP 客户端提供 Python 包管理的命令行访问。

+

功能

+
    +
  • 从 PyPI 安装包
  • +
  • 查看已安装的包
  • +
  • 升级包
  • +
  • 卸载包
  • +
  • 搜索包
  • +
+

常用命令

+
# 安装包
+pip install requests
+
+# 列出已安装的包
+pip list
+
+# 升级包
+pip install --upgrade requests
+
+# 卸载包
+pip uninstall requests
+
+# 搜索包
+pip search json
+
+

使用技巧

+
    +
  • 长按从仪表盘访问 PIP 客户端
  • +
  • 使用 pip help 查看所有可用命令
  • +
  • 某些命令可能需要管理员权限
  • +
+

选择合适的工具

+ + + + + + + + + + + + + + + + + + + + + +
工具最佳用途
Shell 终端快速计算、简单脚本、测试代码片段
IPython复杂探索、数据分析、交互式调试
PIP 客户端安装/更新包、检查依赖
+

了解更多

+ + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/zh/assets/images/favicon.png b/zh/assets/images/favicon.png new file mode 100644 index 0000000..1cf13b9 Binary files /dev/null and b/zh/assets/images/favicon.png differ diff --git a/zh/assets/javascripts/bundle.79ae519e.min.js b/zh/assets/javascripts/bundle.79ae519e.min.js new file mode 100644 index 0000000..3df3e5e --- /dev/null +++ b/zh/assets/javascripts/bundle.79ae519e.min.js @@ -0,0 +1,16 @@ +"use strict";(()=>{var Zi=Object.create;var _r=Object.defineProperty;var ea=Object.getOwnPropertyDescriptor;var ta=Object.getOwnPropertyNames,Bt=Object.getOwnPropertySymbols,ra=Object.getPrototypeOf,Ar=Object.prototype.hasOwnProperty,bo=Object.prototype.propertyIsEnumerable;var ho=(e,t,r)=>t in e?_r(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,P=(e,t)=>{for(var r in t||(t={}))Ar.call(t,r)&&ho(e,r,t[r]);if(Bt)for(var r of Bt(t))bo.call(t,r)&&ho(e,r,t[r]);return e};var vo=(e,t)=>{var r={};for(var o in e)Ar.call(e,o)&&t.indexOf(o)<0&&(r[o]=e[o]);if(e!=null&&Bt)for(var o of Bt(e))t.indexOf(o)<0&&bo.call(e,o)&&(r[o]=e[o]);return r};var Cr=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var oa=(e,t,r,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of ta(t))!Ar.call(e,n)&&n!==r&&_r(e,n,{get:()=>t[n],enumerable:!(o=ea(t,n))||o.enumerable});return e};var $t=(e,t,r)=>(r=e!=null?Zi(ra(e)):{},oa(t||!e||!e.__esModule?_r(r,"default",{value:e,enumerable:!0}):r,e));var go=(e,t,r)=>new Promise((o,n)=>{var i=c=>{try{a(r.next(c))}catch(p){n(p)}},s=c=>{try{a(r.throw(c))}catch(p){n(p)}},a=c=>c.done?o(c.value):Promise.resolve(c.value).then(i,s);a((r=r.apply(e,t)).next())});var xo=Cr((kr,yo)=>{(function(e,t){typeof kr=="object"&&typeof yo!="undefined"?t():typeof define=="function"&&define.amd?define(t):t()})(kr,(function(){"use strict";function e(r){var o=!0,n=!1,i=null,s={text:!0,search:!0,url:!0,tel:!0,email:!0,password:!0,number:!0,date:!0,month:!0,week:!0,time:!0,datetime:!0,"datetime-local":!0};function a(k){return!!(k&&k!==document&&k.nodeName!=="HTML"&&k.nodeName!=="BODY"&&"classList"in k&&"contains"in k.classList)}function c(k){var ut=k.type,je=k.tagName;return!!(je==="INPUT"&&s[ut]&&!k.readOnly||je==="TEXTAREA"&&!k.readOnly||k.isContentEditable)}function p(k){k.classList.contains("focus-visible")||(k.classList.add("focus-visible"),k.setAttribute("data-focus-visible-added",""))}function l(k){k.hasAttribute("data-focus-visible-added")&&(k.classList.remove("focus-visible"),k.removeAttribute("data-focus-visible-added"))}function f(k){k.metaKey||k.altKey||k.ctrlKey||(a(r.activeElement)&&p(r.activeElement),o=!0)}function u(k){o=!1}function d(k){a(k.target)&&(o||c(k.target))&&p(k.target)}function v(k){a(k.target)&&(k.target.classList.contains("focus-visible")||k.target.hasAttribute("data-focus-visible-added"))&&(n=!0,window.clearTimeout(i),i=window.setTimeout(function(){n=!1},100),l(k.target))}function S(k){document.visibilityState==="hidden"&&(n&&(o=!0),X())}function X(){document.addEventListener("mousemove",ee),document.addEventListener("mousedown",ee),document.addEventListener("mouseup",ee),document.addEventListener("pointermove",ee),document.addEventListener("pointerdown",ee),document.addEventListener("pointerup",ee),document.addEventListener("touchmove",ee),document.addEventListener("touchstart",ee),document.addEventListener("touchend",ee)}function re(){document.removeEventListener("mousemove",ee),document.removeEventListener("mousedown",ee),document.removeEventListener("mouseup",ee),document.removeEventListener("pointermove",ee),document.removeEventListener("pointerdown",ee),document.removeEventListener("pointerup",ee),document.removeEventListener("touchmove",ee),document.removeEventListener("touchstart",ee),document.removeEventListener("touchend",ee)}function ee(k){k.target.nodeName&&k.target.nodeName.toLowerCase()==="html"||(o=!1,re())}document.addEventListener("keydown",f,!0),document.addEventListener("mousedown",u,!0),document.addEventListener("pointerdown",u,!0),document.addEventListener("touchstart",u,!0),document.addEventListener("visibilitychange",S,!0),X(),r.addEventListener("focus",d,!0),r.addEventListener("blur",v,!0),r.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&r.host?r.host.setAttribute("data-js-focus-visible",""):r.nodeType===Node.DOCUMENT_NODE&&(document.documentElement.classList.add("js-focus-visible"),document.documentElement.setAttribute("data-js-focus-visible",""))}if(typeof window!="undefined"&&typeof document!="undefined"){window.applyFocusVisiblePolyfill=e;var t;try{t=new CustomEvent("focus-visible-polyfill-ready")}catch(r){t=document.createEvent("CustomEvent"),t.initCustomEvent("focus-visible-polyfill-ready",!1,!1,{})}window.dispatchEvent(t)}typeof document!="undefined"&&e(document)}))});var ro=Cr((jy,Rn)=>{"use strict";/*! + * escape-html + * Copyright(c) 2012-2013 TJ Holowaychuk + * Copyright(c) 2015 Andreas Lubbe + * Copyright(c) 2015 Tiancheng "Timothy" Gu + * MIT Licensed + */var qa=/["'&<>]/;Rn.exports=Ka;function Ka(e){var t=""+e,r=qa.exec(t);if(!r)return t;var o,n="",i=0,s=0;for(i=r.index;i{/*! + * clipboard.js v2.0.11 + * https://clipboardjs.com/ + * + * Licensed MIT © Zeno Rocha + */(function(t,r){typeof Nt=="object"&&typeof io=="object"?io.exports=r():typeof define=="function"&&define.amd?define([],r):typeof Nt=="object"?Nt.ClipboardJS=r():t.ClipboardJS=r()})(Nt,function(){return(function(){var e={686:(function(o,n,i){"use strict";i.d(n,{default:function(){return Xi}});var s=i(279),a=i.n(s),c=i(370),p=i.n(c),l=i(817),f=i.n(l);function u(q){try{return document.execCommand(q)}catch(C){return!1}}var d=function(C){var _=f()(C);return u("cut"),_},v=d;function S(q){var C=document.documentElement.getAttribute("dir")==="rtl",_=document.createElement("textarea");_.style.fontSize="12pt",_.style.border="0",_.style.padding="0",_.style.margin="0",_.style.position="absolute",_.style[C?"right":"left"]="-9999px";var D=window.pageYOffset||document.documentElement.scrollTop;return _.style.top="".concat(D,"px"),_.setAttribute("readonly",""),_.value=q,_}var X=function(C,_){var D=S(C);_.container.appendChild(D);var N=f()(D);return u("copy"),D.remove(),N},re=function(C){var _=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body},D="";return typeof C=="string"?D=X(C,_):C instanceof HTMLInputElement&&!["text","search","url","tel","password"].includes(C==null?void 0:C.type)?D=X(C.value,_):(D=f()(C),u("copy")),D},ee=re;function k(q){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?k=function(_){return typeof _}:k=function(_){return _&&typeof Symbol=="function"&&_.constructor===Symbol&&_!==Symbol.prototype?"symbol":typeof _},k(q)}var ut=function(){var C=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},_=C.action,D=_===void 0?"copy":_,N=C.container,G=C.target,We=C.text;if(D!=="copy"&&D!=="cut")throw new Error('Invalid "action" value, use either "copy" or "cut"');if(G!==void 0)if(G&&k(G)==="object"&&G.nodeType===1){if(D==="copy"&&G.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if(D==="cut"&&(G.hasAttribute("readonly")||G.hasAttribute("disabled")))throw new Error(`Invalid "target" attribute. You can't cut text from elements with "readonly" or "disabled" attributes`)}else throw new Error('Invalid "target" value, use a valid Element');if(We)return ee(We,{container:N});if(G)return D==="cut"?v(G):ee(G,{container:N})},je=ut;function R(q){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?R=function(_){return typeof _}:R=function(_){return _&&typeof Symbol=="function"&&_.constructor===Symbol&&_!==Symbol.prototype?"symbol":typeof _},R(q)}function se(q,C){if(!(q instanceof C))throw new TypeError("Cannot call a class as a function")}function ce(q,C){for(var _=0;_0&&arguments[0]!==void 0?arguments[0]:{};this.action=typeof N.action=="function"?N.action:this.defaultAction,this.target=typeof N.target=="function"?N.target:this.defaultTarget,this.text=typeof N.text=="function"?N.text:this.defaultText,this.container=R(N.container)==="object"?N.container:document.body}},{key:"listenClick",value:function(N){var G=this;this.listener=p()(N,"click",function(We){return G.onClick(We)})}},{key:"onClick",value:function(N){var G=N.delegateTarget||N.currentTarget,We=this.action(G)||"copy",Yt=je({action:We,container:this.container,target:this.target(G),text:this.text(G)});this.emit(Yt?"success":"error",{action:We,text:Yt,trigger:G,clearSelection:function(){G&&G.focus(),window.getSelection().removeAllRanges()}})}},{key:"defaultAction",value:function(N){return Mr("action",N)}},{key:"defaultTarget",value:function(N){var G=Mr("target",N);if(G)return document.querySelector(G)}},{key:"defaultText",value:function(N){return Mr("text",N)}},{key:"destroy",value:function(){this.listener.destroy()}}],[{key:"copy",value:function(N){var G=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body};return ee(N,G)}},{key:"cut",value:function(N){return v(N)}},{key:"isSupported",value:function(){var N=arguments.length>0&&arguments[0]!==void 0?arguments[0]:["copy","cut"],G=typeof N=="string"?[N]:N,We=!!document.queryCommandSupported;return G.forEach(function(Yt){We=We&&!!document.queryCommandSupported(Yt)}),We}}]),_})(a()),Xi=Ji}),828:(function(o){var n=9;if(typeof Element!="undefined"&&!Element.prototype.matches){var i=Element.prototype;i.matches=i.matchesSelector||i.mozMatchesSelector||i.msMatchesSelector||i.oMatchesSelector||i.webkitMatchesSelector}function s(a,c){for(;a&&a.nodeType!==n;){if(typeof a.matches=="function"&&a.matches(c))return a;a=a.parentNode}}o.exports=s}),438:(function(o,n,i){var s=i(828);function a(l,f,u,d,v){var S=p.apply(this,arguments);return l.addEventListener(u,S,v),{destroy:function(){l.removeEventListener(u,S,v)}}}function c(l,f,u,d,v){return typeof l.addEventListener=="function"?a.apply(null,arguments):typeof u=="function"?a.bind(null,document).apply(null,arguments):(typeof l=="string"&&(l=document.querySelectorAll(l)),Array.prototype.map.call(l,function(S){return a(S,f,u,d,v)}))}function p(l,f,u,d){return function(v){v.delegateTarget=s(v.target,f),v.delegateTarget&&d.call(l,v)}}o.exports=c}),879:(function(o,n){n.node=function(i){return i!==void 0&&i instanceof HTMLElement&&i.nodeType===1},n.nodeList=function(i){var s=Object.prototype.toString.call(i);return i!==void 0&&(s==="[object NodeList]"||s==="[object HTMLCollection]")&&"length"in i&&(i.length===0||n.node(i[0]))},n.string=function(i){return typeof i=="string"||i instanceof String},n.fn=function(i){var s=Object.prototype.toString.call(i);return s==="[object Function]"}}),370:(function(o,n,i){var s=i(879),a=i(438);function c(u,d,v){if(!u&&!d&&!v)throw new Error("Missing required arguments");if(!s.string(d))throw new TypeError("Second argument must be a String");if(!s.fn(v))throw new TypeError("Third argument must be a Function");if(s.node(u))return p(u,d,v);if(s.nodeList(u))return l(u,d,v);if(s.string(u))return f(u,d,v);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}function p(u,d,v){return u.addEventListener(d,v),{destroy:function(){u.removeEventListener(d,v)}}}function l(u,d,v){return Array.prototype.forEach.call(u,function(S){S.addEventListener(d,v)}),{destroy:function(){Array.prototype.forEach.call(u,function(S){S.removeEventListener(d,v)})}}}function f(u,d,v){return a(document.body,u,d,v)}o.exports=c}),817:(function(o){function n(i){var s;if(i.nodeName==="SELECT")i.focus(),s=i.value;else if(i.nodeName==="INPUT"||i.nodeName==="TEXTAREA"){var a=i.hasAttribute("readonly");a||i.setAttribute("readonly",""),i.select(),i.setSelectionRange(0,i.value.length),a||i.removeAttribute("readonly"),s=i.value}else{i.hasAttribute("contenteditable")&&i.focus();var c=window.getSelection(),p=document.createRange();p.selectNodeContents(i),c.removeAllRanges(),c.addRange(p),s=c.toString()}return s}o.exports=n}),279:(function(o){function n(){}n.prototype={on:function(i,s,a){var c=this.e||(this.e={});return(c[i]||(c[i]=[])).push({fn:s,ctx:a}),this},once:function(i,s,a){var c=this;function p(){c.off(i,p),s.apply(a,arguments)}return p._=s,this.on(i,p,a)},emit:function(i){var s=[].slice.call(arguments,1),a=((this.e||(this.e={}))[i]||[]).slice(),c=0,p=a.length;for(c;c0&&i[i.length-1])&&(p[0]===6||p[0]===2)){r=0;continue}if(p[0]===3&&(!i||p[1]>i[0]&&p[1]=e.length&&(e=void 0),{value:e&&e[o++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function K(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var o=r.call(e),n,i=[],s;try{for(;(t===void 0||t-- >0)&&!(n=o.next()).done;)i.push(n.value)}catch(a){s={error:a}}finally{try{n&&!n.done&&(r=o.return)&&r.call(o)}finally{if(s)throw s.error}}return i}function B(e,t,r){if(r||arguments.length===2)for(var o=0,n=t.length,i;o1||c(d,S)})},v&&(n[d]=v(n[d])))}function c(d,v){try{p(o[d](v))}catch(S){u(i[0][3],S)}}function p(d){d.value instanceof dt?Promise.resolve(d.value.v).then(l,f):u(i[0][2],d)}function l(d){c("next",d)}function f(d){c("throw",d)}function u(d,v){d(v),i.shift(),i.length&&c(i[0][0],i[0][1])}}function To(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var t=e[Symbol.asyncIterator],r;return t?t.call(e):(e=typeof Oe=="function"?Oe(e):e[Symbol.iterator](),r={},o("next"),o("throw"),o("return"),r[Symbol.asyncIterator]=function(){return this},r);function o(i){r[i]=e[i]&&function(s){return new Promise(function(a,c){s=e[i](s),n(a,c,s.done,s.value)})}}function n(i,s,a,c){Promise.resolve(c).then(function(p){i({value:p,done:a})},s)}}function I(e){return typeof e=="function"}function yt(e){var t=function(o){Error.call(o),o.stack=new Error().stack},r=e(t);return r.prototype=Object.create(Error.prototype),r.prototype.constructor=r,r}var Jt=yt(function(e){return function(r){e(this),this.message=r?r.length+` errors occurred during unsubscription: +`+r.map(function(o,n){return n+1+") "+o.toString()}).join(` + `):"",this.name="UnsubscriptionError",this.errors=r}});function Ze(e,t){if(e){var r=e.indexOf(t);0<=r&&e.splice(r,1)}}var qe=(function(){function e(t){this.initialTeardown=t,this.closed=!1,this._parentage=null,this._finalizers=null}return e.prototype.unsubscribe=function(){var t,r,o,n,i;if(!this.closed){this.closed=!0;var s=this._parentage;if(s)if(this._parentage=null,Array.isArray(s))try{for(var a=Oe(s),c=a.next();!c.done;c=a.next()){var p=c.value;p.remove(this)}}catch(S){t={error:S}}finally{try{c&&!c.done&&(r=a.return)&&r.call(a)}finally{if(t)throw t.error}}else s.remove(this);var l=this.initialTeardown;if(I(l))try{l()}catch(S){i=S instanceof Jt?S.errors:[S]}var f=this._finalizers;if(f){this._finalizers=null;try{for(var u=Oe(f),d=u.next();!d.done;d=u.next()){var v=d.value;try{So(v)}catch(S){i=i!=null?i:[],S instanceof Jt?i=B(B([],K(i)),K(S.errors)):i.push(S)}}}catch(S){o={error:S}}finally{try{d&&!d.done&&(n=u.return)&&n.call(u)}finally{if(o)throw o.error}}}if(i)throw new Jt(i)}},e.prototype.add=function(t){var r;if(t&&t!==this)if(this.closed)So(t);else{if(t instanceof e){if(t.closed||t._hasParent(this))return;t._addParent(this)}(this._finalizers=(r=this._finalizers)!==null&&r!==void 0?r:[]).push(t)}},e.prototype._hasParent=function(t){var r=this._parentage;return r===t||Array.isArray(r)&&r.includes(t)},e.prototype._addParent=function(t){var r=this._parentage;this._parentage=Array.isArray(r)?(r.push(t),r):r?[r,t]:t},e.prototype._removeParent=function(t){var r=this._parentage;r===t?this._parentage=null:Array.isArray(r)&&Ze(r,t)},e.prototype.remove=function(t){var r=this._finalizers;r&&Ze(r,t),t instanceof e&&t._removeParent(this)},e.EMPTY=(function(){var t=new e;return t.closed=!0,t})(),e})();var $r=qe.EMPTY;function Xt(e){return e instanceof qe||e&&"closed"in e&&I(e.remove)&&I(e.add)&&I(e.unsubscribe)}function So(e){I(e)?e():e.unsubscribe()}var De={onUnhandledError:null,onStoppedNotification:null,Promise:void 0,useDeprecatedSynchronousErrorHandling:!1,useDeprecatedNextContext:!1};var xt={setTimeout:function(e,t){for(var r=[],o=2;o0},enumerable:!1,configurable:!0}),t.prototype._trySubscribe=function(r){return this._throwIfClosed(),e.prototype._trySubscribe.call(this,r)},t.prototype._subscribe=function(r){return this._throwIfClosed(),this._checkFinalizedStatuses(r),this._innerSubscribe(r)},t.prototype._innerSubscribe=function(r){var o=this,n=this,i=n.hasError,s=n.isStopped,a=n.observers;return i||s?$r:(this.currentObservers=null,a.push(r),new qe(function(){o.currentObservers=null,Ze(a,r)}))},t.prototype._checkFinalizedStatuses=function(r){var o=this,n=o.hasError,i=o.thrownError,s=o.isStopped;n?r.error(i):s&&r.complete()},t.prototype.asObservable=function(){var r=new F;return r.source=this,r},t.create=function(r,o){return new Ho(r,o)},t})(F);var Ho=(function(e){ie(t,e);function t(r,o){var n=e.call(this)||this;return n.destination=r,n.source=o,n}return t.prototype.next=function(r){var o,n;(n=(o=this.destination)===null||o===void 0?void 0:o.next)===null||n===void 0||n.call(o,r)},t.prototype.error=function(r){var o,n;(n=(o=this.destination)===null||o===void 0?void 0:o.error)===null||n===void 0||n.call(o,r)},t.prototype.complete=function(){var r,o;(o=(r=this.destination)===null||r===void 0?void 0:r.complete)===null||o===void 0||o.call(r)},t.prototype._subscribe=function(r){var o,n;return(n=(o=this.source)===null||o===void 0?void 0:o.subscribe(r))!==null&&n!==void 0?n:$r},t})(T);var jr=(function(e){ie(t,e);function t(r){var o=e.call(this)||this;return o._value=r,o}return Object.defineProperty(t.prototype,"value",{get:function(){return this.getValue()},enumerable:!1,configurable:!0}),t.prototype._subscribe=function(r){var o=e.prototype._subscribe.call(this,r);return!o.closed&&r.next(this._value),o},t.prototype.getValue=function(){var r=this,o=r.hasError,n=r.thrownError,i=r._value;if(o)throw n;return this._throwIfClosed(),i},t.prototype.next=function(r){e.prototype.next.call(this,this._value=r)},t})(T);var Rt={now:function(){return(Rt.delegate||Date).now()},delegate:void 0};var It=(function(e){ie(t,e);function t(r,o,n){r===void 0&&(r=1/0),o===void 0&&(o=1/0),n===void 0&&(n=Rt);var i=e.call(this)||this;return i._bufferSize=r,i._windowTime=o,i._timestampProvider=n,i._buffer=[],i._infiniteTimeWindow=!0,i._infiniteTimeWindow=o===1/0,i._bufferSize=Math.max(1,r),i._windowTime=Math.max(1,o),i}return t.prototype.next=function(r){var o=this,n=o.isStopped,i=o._buffer,s=o._infiniteTimeWindow,a=o._timestampProvider,c=o._windowTime;n||(i.push(r),!s&&i.push(a.now()+c)),this._trimBuffer(),e.prototype.next.call(this,r)},t.prototype._subscribe=function(r){this._throwIfClosed(),this._trimBuffer();for(var o=this._innerSubscribe(r),n=this,i=n._infiniteTimeWindow,s=n._buffer,a=s.slice(),c=0;c0?e.prototype.schedule.call(this,r,o):(this.delay=o,this.state=r,this.scheduler.flush(this),this)},t.prototype.execute=function(r,o){return o>0||this.closed?e.prototype.execute.call(this,r,o):this._execute(r,o)},t.prototype.requestAsyncId=function(r,o,n){return n===void 0&&(n=0),n!=null&&n>0||n==null&&this.delay>0?e.prototype.requestAsyncId.call(this,r,o,n):(r.flush(this),0)},t})(St);var Ro=(function(e){ie(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t})(Ot);var Dr=new Ro(Po);var Io=(function(e){ie(t,e);function t(r,o){var n=e.call(this,r,o)||this;return n.scheduler=r,n.work=o,n}return t.prototype.requestAsyncId=function(r,o,n){return n===void 0&&(n=0),n!==null&&n>0?e.prototype.requestAsyncId.call(this,r,o,n):(r.actions.push(this),r._scheduled||(r._scheduled=Tt.requestAnimationFrame(function(){return r.flush(void 0)})))},t.prototype.recycleAsyncId=function(r,o,n){var i;if(n===void 0&&(n=0),n!=null?n>0:this.delay>0)return e.prototype.recycleAsyncId.call(this,r,o,n);var s=r.actions;o!=null&&o===r._scheduled&&((i=s[s.length-1])===null||i===void 0?void 0:i.id)!==o&&(Tt.cancelAnimationFrame(o),r._scheduled=void 0)},t})(St);var Fo=(function(e){ie(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t.prototype.flush=function(r){this._active=!0;var o;r?o=r.id:(o=this._scheduled,this._scheduled=void 0);var n=this.actions,i;r=r||n.shift();do if(i=r.execute(r.state,r.delay))break;while((r=n[0])&&r.id===o&&n.shift());if(this._active=!1,i){for(;(r=n[0])&&r.id===o&&n.shift();)r.unsubscribe();throw i}},t})(Ot);var ye=new Fo(Io);var y=new F(function(e){return e.complete()});function tr(e){return e&&I(e.schedule)}function Vr(e){return e[e.length-1]}function pt(e){return I(Vr(e))?e.pop():void 0}function Fe(e){return tr(Vr(e))?e.pop():void 0}function rr(e,t){return typeof Vr(e)=="number"?e.pop():t}var Lt=(function(e){return e&&typeof e.length=="number"&&typeof e!="function"});function or(e){return I(e==null?void 0:e.then)}function nr(e){return I(e[wt])}function ir(e){return Symbol.asyncIterator&&I(e==null?void 0:e[Symbol.asyncIterator])}function ar(e){return new TypeError("You provided "+(e!==null&&typeof e=="object"?"an invalid object":"'"+e+"'")+" where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.")}function fa(){return typeof Symbol!="function"||!Symbol.iterator?"@@iterator":Symbol.iterator}var sr=fa();function cr(e){return I(e==null?void 0:e[sr])}function pr(e){return wo(this,arguments,function(){var r,o,n,i;return Gt(this,function(s){switch(s.label){case 0:r=e.getReader(),s.label=1;case 1:s.trys.push([1,,9,10]),s.label=2;case 2:return[4,dt(r.read())];case 3:return o=s.sent(),n=o.value,i=o.done,i?[4,dt(void 0)]:[3,5];case 4:return[2,s.sent()];case 5:return[4,dt(n)];case 6:return[4,s.sent()];case 7:return s.sent(),[3,2];case 8:return[3,10];case 9:return r.releaseLock(),[7];case 10:return[2]}})})}function lr(e){return I(e==null?void 0:e.getReader)}function U(e){if(e instanceof F)return e;if(e!=null){if(nr(e))return ua(e);if(Lt(e))return da(e);if(or(e))return ha(e);if(ir(e))return jo(e);if(cr(e))return ba(e);if(lr(e))return va(e)}throw ar(e)}function ua(e){return new F(function(t){var r=e[wt]();if(I(r.subscribe))return r.subscribe(t);throw new TypeError("Provided object does not correctly implement Symbol.observable")})}function da(e){return new F(function(t){for(var r=0;r=2;return function(o){return o.pipe(e?g(function(n,i){return e(n,i,o)}):be,Ee(1),r?Qe(t):tn(function(){return new fr}))}}function Yr(e){return e<=0?function(){return y}:E(function(t,r){var o=[];t.subscribe(w(r,function(n){o.push(n),e=2,!0))}function le(e){e===void 0&&(e={});var t=e.connector,r=t===void 0?function(){return new T}:t,o=e.resetOnError,n=o===void 0?!0:o,i=e.resetOnComplete,s=i===void 0?!0:i,a=e.resetOnRefCountZero,c=a===void 0?!0:a;return function(p){var l,f,u,d=0,v=!1,S=!1,X=function(){f==null||f.unsubscribe(),f=void 0},re=function(){X(),l=u=void 0,v=S=!1},ee=function(){var k=l;re(),k==null||k.unsubscribe()};return E(function(k,ut){d++,!S&&!v&&X();var je=u=u!=null?u:r();ut.add(function(){d--,d===0&&!S&&!v&&(f=Br(ee,c))}),je.subscribe(ut),!l&&d>0&&(l=new bt({next:function(R){return je.next(R)},error:function(R){S=!0,X(),f=Br(re,n,R),je.error(R)},complete:function(){v=!0,X(),f=Br(re,s),je.complete()}}),U(k).subscribe(l))})(p)}}function Br(e,t){for(var r=[],o=2;oe.next(document)),e}function M(e,t=document){return Array.from(t.querySelectorAll(e))}function j(e,t=document){let r=ue(e,t);if(typeof r=="undefined")throw new ReferenceError(`Missing element: expected "${e}" to be present`);return r}function ue(e,t=document){return t.querySelector(e)||void 0}function Ne(){var e,t,r,o;return(o=(r=(t=(e=document.activeElement)==null?void 0:e.shadowRoot)==null?void 0:t.activeElement)!=null?r:document.activeElement)!=null?o:void 0}var Ra=L(h(document.body,"focusin"),h(document.body,"focusout")).pipe(Ae(1),Q(void 0),m(()=>Ne()||document.body),Z(1));function Ye(e){return Ra.pipe(m(t=>e.contains(t)),Y())}function it(e,t){return H(()=>L(h(e,"mouseenter").pipe(m(()=>!0)),h(e,"mouseleave").pipe(m(()=>!1))).pipe(t?jt(r=>He(+!r*t)):be,Q(e.matches(":hover"))))}function sn(e,t){if(typeof t=="string"||typeof t=="number")e.innerHTML+=t.toString();else if(t instanceof Node)e.appendChild(t);else if(Array.isArray(t))for(let r of t)sn(e,r)}function x(e,t,...r){let o=document.createElement(e);if(t)for(let n of Object.keys(t))typeof t[n]!="undefined"&&(typeof t[n]!="boolean"?o.setAttribute(n,t[n]):o.setAttribute(n,""));for(let n of r)sn(o,n);return o}function br(e){if(e>999){let t=+((e-950)%1e3>99);return`${((e+1e-6)/1e3).toFixed(t)}k`}else return e.toString()}function _t(e){let t=x("script",{src:e});return H(()=>(document.head.appendChild(t),L(h(t,"load"),h(t,"error").pipe(b(()=>Nr(()=>new ReferenceError(`Invalid script: ${e}`))))).pipe(m(()=>{}),A(()=>document.head.removeChild(t)),Ee(1))))}var cn=new T,Ia=H(()=>typeof ResizeObserver=="undefined"?_t("https://unpkg.com/resize-observer-polyfill"):$(void 0)).pipe(m(()=>new ResizeObserver(e=>e.forEach(t=>cn.next(t)))),b(e=>L(tt,$(e)).pipe(A(()=>e.disconnect()))),Z(1));function de(e){return{width:e.offsetWidth,height:e.offsetHeight}}function Le(e){let t=e;for(;t.clientWidth===0&&t.parentElement;)t=t.parentElement;return Ia.pipe(O(r=>r.observe(t)),b(r=>cn.pipe(g(o=>o.target===t),A(()=>r.unobserve(t)))),m(()=>de(e)),Q(de(e)))}function At(e){return{width:e.scrollWidth,height:e.scrollHeight}}function vr(e){let t=e.parentElement;for(;t&&(e.scrollWidth<=t.scrollWidth&&e.scrollHeight<=t.scrollHeight);)t=(e=t).parentElement;return t?e:void 0}function pn(e){let t=[],r=e.parentElement;for(;r;)(e.clientWidth>r.clientWidth||e.clientHeight>r.clientHeight)&&t.push(r),r=(e=r).parentElement;return t.length===0&&t.push(document.documentElement),t}function Be(e){return{x:e.offsetLeft,y:e.offsetTop}}function ln(e){let t=e.getBoundingClientRect();return{x:t.x+window.scrollX,y:t.y+window.scrollY}}function mn(e){return L(h(window,"load"),h(window,"resize")).pipe($e(0,ye),m(()=>Be(e)),Q(Be(e)))}function gr(e){return{x:e.scrollLeft,y:e.scrollTop}}function Ge(e){return L(h(e,"scroll"),h(window,"scroll"),h(window,"resize")).pipe($e(0,ye),m(()=>gr(e)),Q(gr(e)))}var fn=new T,Fa=H(()=>$(new IntersectionObserver(e=>{for(let t of e)fn.next(t)},{threshold:0}))).pipe(b(e=>L(tt,$(e)).pipe(A(()=>e.disconnect()))),Z(1));function mt(e){return Fa.pipe(O(t=>t.observe(e)),b(t=>fn.pipe(g(({target:r})=>r===e),A(()=>t.unobserve(e)),m(({isIntersecting:r})=>r))))}function un(e,t=16){return Ge(e).pipe(m(({y:r})=>{let o=de(e),n=At(e);return r>=n.height-o.height-t}),Y())}var yr={drawer:j("[data-md-toggle=drawer]"),search:j("[data-md-toggle=search]")};function dn(e){return yr[e].checked}function at(e,t){yr[e].checked!==t&&yr[e].click()}function Je(e){let t=yr[e];return h(t,"change").pipe(m(()=>t.checked),Q(t.checked))}function ja(e,t){switch(e.constructor){case HTMLInputElement:return e.type==="radio"?/^Arrow/.test(t):!0;case HTMLSelectElement:case HTMLTextAreaElement:return!0;default:return e.isContentEditable}}function Ua(){return L(h(window,"compositionstart").pipe(m(()=>!0)),h(window,"compositionend").pipe(m(()=>!1))).pipe(Q(!1))}function hn(){let e=h(window,"keydown").pipe(g(t=>!(t.metaKey||t.ctrlKey)),m(t=>({mode:dn("search")?"search":"global",type:t.key,claim(){t.preventDefault(),t.stopPropagation()}})),g(({mode:t,type:r})=>{if(t==="global"){let o=Ne();if(typeof o!="undefined")return!ja(o,r)}return!0}),le());return Ua().pipe(b(t=>t?y:e))}function we(){return new URL(location.href)}function st(e,t=!1){if(V("navigation.instant")&&!t){let r=x("a",{href:e.href});document.body.appendChild(r),r.click(),r.remove()}else location.href=e.href}function bn(){return new T}function vn(){return location.hash.slice(1)}function gn(e){let t=x("a",{href:e});t.addEventListener("click",r=>r.stopPropagation()),t.click()}function Zr(e){return L(h(window,"hashchange"),e).pipe(m(vn),Q(vn()),g(t=>t.length>0),Z(1))}function yn(e){return Zr(e).pipe(m(t=>ue(`[id="${t}"]`)),g(t=>typeof t!="undefined"))}function Wt(e){let t=matchMedia(e);return ur(r=>t.addListener(()=>r(t.matches))).pipe(Q(t.matches))}function xn(){let e=matchMedia("print");return L(h(window,"beforeprint").pipe(m(()=>!0)),h(window,"afterprint").pipe(m(()=>!1))).pipe(Q(e.matches))}function eo(e,t){return e.pipe(b(r=>r?t():y))}function to(e,t){return new F(r=>{let o=new XMLHttpRequest;return o.open("GET",`${e}`),o.responseType="blob",o.addEventListener("load",()=>{o.status>=200&&o.status<300?(r.next(o.response),r.complete()):r.error(new Error(o.statusText))}),o.addEventListener("error",()=>{r.error(new Error("Network error"))}),o.addEventListener("abort",()=>{r.complete()}),typeof(t==null?void 0:t.progress$)!="undefined"&&(o.addEventListener("progress",n=>{var i;if(n.lengthComputable)t.progress$.next(n.loaded/n.total*100);else{let s=(i=o.getResponseHeader("Content-Length"))!=null?i:0;t.progress$.next(n.loaded/+s*100)}}),t.progress$.next(5)),o.send(),()=>o.abort()})}function ze(e,t){return to(e,t).pipe(b(r=>r.text()),m(r=>JSON.parse(r)),Z(1))}function xr(e,t){let r=new DOMParser;return to(e,t).pipe(b(o=>o.text()),m(o=>r.parseFromString(o,"text/html")),Z(1))}function En(e,t){let r=new DOMParser;return to(e,t).pipe(b(o=>o.text()),m(o=>r.parseFromString(o,"text/xml")),Z(1))}function wn(){return{x:Math.max(0,scrollX),y:Math.max(0,scrollY)}}function Tn(){return L(h(window,"scroll",{passive:!0}),h(window,"resize",{passive:!0})).pipe(m(wn),Q(wn()))}function Sn(){return{width:innerWidth,height:innerHeight}}function On(){return h(window,"resize",{passive:!0}).pipe(m(Sn),Q(Sn()))}function Ln(){return z([Tn(),On()]).pipe(m(([e,t])=>({offset:e,size:t})),Z(1))}function Er(e,{viewport$:t,header$:r}){let o=t.pipe(ne("size")),n=z([o,r]).pipe(m(()=>Be(e)));return z([r,t,n]).pipe(m(([{height:i},{offset:s,size:a},{x:c,y:p}])=>({offset:{x:s.x-c,y:s.y-p+i},size:a})))}function Wa(e){return h(e,"message",t=>t.data)}function Da(e){let t=new T;return t.subscribe(r=>e.postMessage(r)),t}function Mn(e,t=new Worker(e)){let r=Wa(t),o=Da(t),n=new T;n.subscribe(o);let i=o.pipe(oe(),ae(!0));return n.pipe(oe(),Ve(r.pipe(W(i))),le())}var Va=j("#__config"),Ct=JSON.parse(Va.textContent);Ct.base=`${new URL(Ct.base,we())}`;function Te(){return Ct}function V(e){return Ct.features.includes(e)}function Me(e,t){return typeof t!="undefined"?Ct.translations[e].replace("#",t.toString()):Ct.translations[e]}function Ce(e,t=document){return j(`[data-md-component=${e}]`,t)}function me(e,t=document){return M(`[data-md-component=${e}]`,t)}function Na(e){let t=j(".md-typeset > :first-child",e);return h(t,"click",{once:!0}).pipe(m(()=>j(".md-typeset",e)),m(r=>({hash:__md_hash(r.innerHTML)})))}function _n(e){if(!V("announce.dismiss")||!e.childElementCount)return y;if(!e.hidden){let t=j(".md-typeset",e);__md_hash(t.innerHTML)===__md_get("__announce")&&(e.hidden=!0)}return H(()=>{let t=new T;return t.subscribe(({hash:r})=>{e.hidden=!0,__md_set("__announce",r)}),Na(e).pipe(O(r=>t.next(r)),A(()=>t.complete()),m(r=>P({ref:e},r)))})}function za(e,{target$:t}){return t.pipe(m(r=>({hidden:r!==e})))}function An(e,t){let r=new T;return r.subscribe(({hidden:o})=>{e.hidden=o}),za(e,t).pipe(O(o=>r.next(o)),A(()=>r.complete()),m(o=>P({ref:e},o)))}function Dt(e,t){return t==="inline"?x("div",{class:"md-tooltip md-tooltip--inline",id:e,role:"tooltip"},x("div",{class:"md-tooltip__inner md-typeset"})):x("div",{class:"md-tooltip",id:e,role:"tooltip"},x("div",{class:"md-tooltip__inner md-typeset"}))}function wr(...e){return x("div",{class:"md-tooltip2",role:"dialog"},x("div",{class:"md-tooltip2__inner md-typeset"},e))}function Cn(...e){return x("div",{class:"md-tooltip2",role:"tooltip"},x("div",{class:"md-tooltip2__inner md-typeset"},e))}function kn(e,t){if(t=t?`${t}_annotation_${e}`:void 0,t){let r=t?`#${t}`:void 0;return x("aside",{class:"md-annotation",tabIndex:0},Dt(t),x("a",{href:r,class:"md-annotation__index",tabIndex:-1},x("span",{"data-md-annotation-id":e})))}else return x("aside",{class:"md-annotation",tabIndex:0},Dt(t),x("span",{class:"md-annotation__index",tabIndex:-1},x("span",{"data-md-annotation-id":e})))}function Hn(e){return x("button",{class:"md-code__button",title:Me("clipboard.copy"),"data-clipboard-target":`#${e} > code`,"data-md-type":"copy"})}function $n(){return x("button",{class:"md-code__button",title:"Toggle line selection","data-md-type":"select"})}function Pn(){return x("nav",{class:"md-code__nav"})}var In=$t(ro());function oo(e,t){let r=t&2,o=t&1,n=Object.keys(e.terms).filter(c=>!e.terms[c]).reduce((c,p)=>[...c,x("del",null,(0,In.default)(p))," "],[]).slice(0,-1),i=Te(),s=new URL(e.location,i.base);V("search.highlight")&&s.searchParams.set("h",Object.entries(e.terms).filter(([,c])=>c).reduce((c,[p])=>`${c} ${p}`.trim(),""));let{tags:a}=Te();return x("a",{href:`${s}`,class:"md-search-result__link",tabIndex:-1},x("article",{class:"md-search-result__article md-typeset","data-md-score":e.score.toFixed(2)},r>0&&x("div",{class:"md-search-result__icon md-icon"}),r>0&&x("h1",null,e.title),r<=0&&x("h2",null,e.title),o>0&&e.text.length>0&&e.text,e.tags&&x("nav",{class:"md-tags"},e.tags.map(c=>{let p=a?c in a?`md-tag-icon md-tag--${a[c]}`:"md-tag-icon":"";return x("span",{class:`md-tag ${p}`},c)})),o>0&&n.length>0&&x("p",{class:"md-search-result__terms"},Me("search.result.term.missing"),": ",...n)))}function Fn(e){let t=e[0].score,r=[...e],o=Te(),n=r.findIndex(l=>!`${new URL(l.location,o.base)}`.includes("#")),[i]=r.splice(n,1),s=r.findIndex(l=>l.scoreoo(l,1)),...c.length?[x("details",{class:"md-search-result__more"},x("summary",{tabIndex:-1},x("div",null,c.length>0&&c.length===1?Me("search.result.more.one"):Me("search.result.more.other",c.length))),...c.map(l=>oo(l,1)))]:[]];return x("li",{class:"md-search-result__item"},p)}function jn(e){return x("ul",{class:"md-source__facts"},Object.entries(e).map(([t,r])=>x("li",{class:`md-source__fact md-source__fact--${t}`},typeof r=="number"?br(r):r)))}function no(e){let t=`tabbed-control tabbed-control--${e}`;return x("div",{class:t,hidden:!0},x("button",{class:"tabbed-button",tabIndex:-1,"aria-hidden":"true"}))}function Un(e){return x("div",{class:"md-typeset__scrollwrap"},x("div",{class:"md-typeset__table"},e))}function Qa(e){var o;let t=Te(),r=new URL(`../${e.version}/`,t.base);return x("li",{class:"md-version__item"},x("a",{href:`${r}`,class:"md-version__link"},e.title,((o=t.version)==null?void 0:o.alias)&&e.aliases.length>0&&x("span",{class:"md-version__alias"},e.aliases[0])))}function Wn(e,t){var o;let r=Te();return e=e.filter(n=>{var i;return!((i=n.properties)!=null&&i.hidden)}),x("div",{class:"md-version"},x("button",{class:"md-version__current","aria-label":Me("select.version")},t.title,((o=r.version)==null?void 0:o.alias)&&t.aliases.length>0&&x("span",{class:"md-version__alias"},t.aliases[0])),x("ul",{class:"md-version__list"},e.map(Qa)))}var Ya=0;function Ba(e,t=250){let r=z([Ye(e),it(e,t)]).pipe(m(([n,i])=>n||i),Y()),o=H(()=>pn(e)).pipe(J(Ge),gt(1),Pe(r),m(()=>ln(e)));return r.pipe(Re(n=>n),b(()=>z([r,o])),m(([n,i])=>({active:n,offset:i})),le())}function Vt(e,t,r=250){let{content$:o,viewport$:n}=t,i=`__tooltip2_${Ya++}`;return H(()=>{let s=new T,a=new jr(!1);s.pipe(oe(),ae(!1)).subscribe(a);let c=a.pipe(jt(l=>He(+!l*250,Dr)),Y(),b(l=>l?o:y),O(l=>l.id=i),le());z([s.pipe(m(({active:l})=>l)),c.pipe(b(l=>it(l,250)),Q(!1))]).pipe(m(l=>l.some(f=>f))).subscribe(a);let p=a.pipe(g(l=>l),te(c,n),m(([l,f,{size:u}])=>{let d=e.getBoundingClientRect(),v=d.width/2;if(f.role==="tooltip")return{x:v,y:8+d.height};if(d.y>=u.height/2){let{height:S}=de(f);return{x:v,y:-16-S}}else return{x:v,y:16+d.height}}));return z([c,s,p]).subscribe(([l,{offset:f},u])=>{l.style.setProperty("--md-tooltip-host-x",`${f.x}px`),l.style.setProperty("--md-tooltip-host-y",`${f.y}px`),l.style.setProperty("--md-tooltip-x",`${u.x}px`),l.style.setProperty("--md-tooltip-y",`${u.y}px`),l.classList.toggle("md-tooltip2--top",u.y<0),l.classList.toggle("md-tooltip2--bottom",u.y>=0)}),a.pipe(g(l=>l),te(c,(l,f)=>f),g(l=>l.role==="tooltip")).subscribe(l=>{let f=de(j(":scope > *",l));l.style.setProperty("--md-tooltip-width",`${f.width}px`),l.style.setProperty("--md-tooltip-tail","0px")}),a.pipe(Y(),xe(ye),te(c)).subscribe(([l,f])=>{f.classList.toggle("md-tooltip2--active",l)}),z([a.pipe(g(l=>l)),c]).subscribe(([l,f])=>{f.role==="dialog"?(e.setAttribute("aria-controls",i),e.setAttribute("aria-haspopup","dialog")):e.setAttribute("aria-describedby",i)}),a.pipe(g(l=>!l)).subscribe(()=>{e.removeAttribute("aria-controls"),e.removeAttribute("aria-describedby"),e.removeAttribute("aria-haspopup")}),Ba(e,r).pipe(O(l=>s.next(l)),A(()=>s.complete()),m(l=>P({ref:e},l)))})}function Xe(e,{viewport$:t},r=document.body){return Vt(e,{content$:new F(o=>{let n=e.title,i=Cn(n);return o.next(i),e.removeAttribute("title"),r.append(i),()=>{i.remove(),e.setAttribute("title",n)}}),viewport$:t},0)}function Ga(e,t){let r=H(()=>z([mn(e),Ge(t)])).pipe(m(([{x:o,y:n},i])=>{let{width:s,height:a}=de(e);return{x:o-i.x+s/2,y:n-i.y+a/2}}));return Ye(e).pipe(b(o=>r.pipe(m(n=>({active:o,offset:n})),Ee(+!o||1/0))))}function Dn(e,t,{target$:r}){let[o,n]=Array.from(e.children);return H(()=>{let i=new T,s=i.pipe(oe(),ae(!0));return i.subscribe({next({offset:a}){e.style.setProperty("--md-tooltip-x",`${a.x}px`),e.style.setProperty("--md-tooltip-y",`${a.y}px`)},complete(){e.style.removeProperty("--md-tooltip-x"),e.style.removeProperty("--md-tooltip-y")}}),mt(e).pipe(W(s)).subscribe(a=>{e.toggleAttribute("data-md-visible",a)}),L(i.pipe(g(({active:a})=>a)),i.pipe(Ae(250),g(({active:a})=>!a))).subscribe({next({active:a}){a?e.prepend(o):o.remove()},complete(){e.prepend(o)}}),i.pipe($e(16,ye)).subscribe(({active:a})=>{o.classList.toggle("md-tooltip--active",a)}),i.pipe(gt(125,ye),g(()=>!!e.offsetParent),m(()=>e.offsetParent.getBoundingClientRect()),m(({x:a})=>a)).subscribe({next(a){a?e.style.setProperty("--md-tooltip-0",`${-a}px`):e.style.removeProperty("--md-tooltip-0")},complete(){e.style.removeProperty("--md-tooltip-0")}}),h(n,"click").pipe(W(s),g(a=>!(a.metaKey||a.ctrlKey))).subscribe(a=>{a.stopPropagation(),a.preventDefault()}),h(n,"mousedown").pipe(W(s),te(i)).subscribe(([a,{active:c}])=>{var p;if(a.button!==0||a.metaKey||a.ctrlKey)a.preventDefault();else if(c){a.preventDefault();let l=e.parentElement.closest(".md-annotation");l instanceof HTMLElement?l.focus():(p=Ne())==null||p.blur()}}),r.pipe(W(s),g(a=>a===o),nt(125)).subscribe(()=>e.focus()),Ga(e,t).pipe(O(a=>i.next(a)),A(()=>i.complete()),m(a=>P({ref:e},a)))})}function Ja(e){let t=Te();if(e.tagName!=="CODE")return[e];let r=[".c",".c1",".cm"];if(t.annotate&&typeof t.annotate=="object"){let o=e.closest("[class|=language]");if(o)for(let n of Array.from(o.classList)){if(!n.startsWith("language-"))continue;let[,i]=n.split("-");i in t.annotate&&r.push(...t.annotate[i])}}return M(r.join(", "),e)}function Xa(e){let t=[];for(let r of Ja(e)){let o=[],n=document.createNodeIterator(r,NodeFilter.SHOW_TEXT);for(let i=n.nextNode();i;i=n.nextNode())o.push(i);for(let i of o){let s;for(;s=/(\(\d+\))(!)?/.exec(i.textContent);){let[,a,c]=s;if(typeof c=="undefined"){let p=i.splitText(s.index);i=p.splitText(a.length),t.push(p)}else{i.textContent=a,t.push(i);break}}}}return t}function Vn(e,t){t.append(...Array.from(e.childNodes))}function Tr(e,t,{target$:r,print$:o}){let n=t.closest("[id]"),i=n==null?void 0:n.id,s=new Map;for(let a of Xa(t)){let[,c]=a.textContent.match(/\((\d+)\)/);ue(`:scope > li:nth-child(${c})`,e)&&(s.set(c,kn(c,i)),a.replaceWith(s.get(c)))}return s.size===0?y:H(()=>{let a=new T,c=a.pipe(oe(),ae(!0)),p=[];for(let[l,f]of s)p.push([j(".md-typeset",f),j(`:scope > li:nth-child(${l})`,e)]);return o.pipe(W(c)).subscribe(l=>{e.hidden=!l,e.classList.toggle("md-annotation-list",l);for(let[f,u]of p)l?Vn(f,u):Vn(u,f)}),L(...[...s].map(([,l])=>Dn(l,t,{target$:r}))).pipe(A(()=>a.complete()),le())})}function Nn(e){if(e.nextElementSibling){let t=e.nextElementSibling;if(t.tagName==="OL")return t;if(t.tagName==="P"&&!t.children.length)return Nn(t)}}function zn(e,t){return H(()=>{let r=Nn(e);return typeof r!="undefined"?Tr(r,e,t):y})}var Kn=$t(ao());var Za=0,qn=L(h(window,"keydown").pipe(m(()=>!0)),L(h(window,"keyup"),h(window,"contextmenu")).pipe(m(()=>!1))).pipe(Q(!1),Z(1));function Qn(e){if(e.nextElementSibling){let t=e.nextElementSibling;if(t.tagName==="OL")return t;if(t.tagName==="P"&&!t.children.length)return Qn(t)}}function es(e){return Le(e).pipe(m(({width:t})=>({scrollable:At(e).width>t})),ne("scrollable"))}function Yn(e,t){let{matches:r}=matchMedia("(hover)"),o=H(()=>{let n=new T,i=n.pipe(Yr(1));n.subscribe(({scrollable:d})=>{d&&r?e.setAttribute("tabindex","0"):e.removeAttribute("tabindex")});let s=[],a=e.closest("pre"),c=a.closest("[id]"),p=c?c.id:Za++;a.id=`__code_${p}`;let l=[],f=e.closest(".highlight");if(f instanceof HTMLElement){let d=Qn(f);if(typeof d!="undefined"&&(f.classList.contains("annotate")||V("content.code.annotate"))){let v=Tr(d,e,t);l.push(Le(f).pipe(W(i),m(({width:S,height:X})=>S&&X),Y(),b(S=>S?v:y)))}}let u=M(":scope > span[id]",e);if(u.length&&(e.classList.add("md-code__content"),e.closest(".select")||V("content.code.select")&&!e.closest(".no-select"))){let d=+u[0].id.split("-").pop(),v=$n();s.push(v),V("content.tooltips")&&l.push(Xe(v,{viewport$}));let S=h(v,"click").pipe(Ut(R=>!R,!1),O(()=>v.blur()),le());S.subscribe(R=>{v.classList.toggle("md-code__button--active",R)});let X=fe(u).pipe(J(R=>it(R).pipe(m(se=>[R,se]))));S.pipe(b(R=>R?X:y)).subscribe(([R,se])=>{let ce=ue(".hll.select",R);if(ce&&!se)ce.replaceWith(...Array.from(ce.childNodes));else if(!ce&&se){let he=document.createElement("span");he.className="hll select",he.append(...Array.from(R.childNodes).slice(1)),R.append(he)}});let re=fe(u).pipe(J(R=>h(R,"mousedown").pipe(O(se=>se.preventDefault()),m(()=>R)))),ee=S.pipe(b(R=>R?re:y),te(qn),m(([R,se])=>{var he;let ce=u.indexOf(R)+d;if(se===!1)return[ce,ce];{let Se=M(".hll",e).map(Ue=>u.indexOf(Ue.parentElement)+d);return(he=window.getSelection())==null||he.removeAllRanges(),[Math.min(ce,...Se),Math.max(ce,...Se)]}})),k=Zr(y).pipe(g(R=>R.startsWith(`__codelineno-${p}-`)));k.subscribe(R=>{let[,,se]=R.split("-"),ce=se.split(":").map(Se=>+Se-d+1);ce.length===1&&ce.push(ce[0]);for(let Se of M(".hll:not(.select)",e))Se.replaceWith(...Array.from(Se.childNodes));let he=u.slice(ce[0]-1,ce[1]);for(let Se of he){let Ue=document.createElement("span");Ue.className="hll",Ue.append(...Array.from(Se.childNodes).slice(1)),Se.append(Ue)}}),k.pipe(Ee(1),xe(pe)).subscribe(R=>{if(R.includes(":")){let se=document.getElementById(R.split(":")[0]);se&&setTimeout(()=>{let ce=se,he=-64;for(;ce!==document.body;)he+=ce.offsetTop,ce=ce.offsetParent;window.scrollTo({top:he})},1)}});let je=fe(M('a[href^="#__codelineno"]',f)).pipe(J(R=>h(R,"click").pipe(O(se=>se.preventDefault()),m(()=>R)))).pipe(W(i),te(qn),m(([R,se])=>{let he=+j(`[id="${R.hash.slice(1)}"]`).parentElement.id.split("-").pop();if(se===!1)return[he,he];{let Se=M(".hll",e).map(Ue=>+Ue.parentElement.id.split("-").pop());return[Math.min(he,...Se),Math.max(he,...Se)]}}));L(ee,je).subscribe(R=>{let se=`#__codelineno-${p}-`;R[0]===R[1]?se+=R[0]:se+=`${R[0]}:${R[1]}`,history.replaceState({},"",se),window.dispatchEvent(new HashChangeEvent("hashchange",{newURL:window.location.origin+window.location.pathname+se,oldURL:window.location.href}))})}if(Kn.default.isSupported()&&(e.closest(".copy")||V("content.code.copy")&&!e.closest(".no-copy"))){let d=Hn(a.id);s.push(d),V("content.tooltips")&&l.push(Xe(d,{viewport$}))}if(s.length){let d=Pn();d.append(...s),a.insertBefore(d,e)}return es(e).pipe(O(d=>n.next(d)),A(()=>n.complete()),m(d=>P({ref:e},d)),Ve(L(...l).pipe(W(i))))});return V("content.lazy")?mt(e).pipe(g(n=>n),Ee(1),b(()=>o)):o}function ts(e,{target$:t,print$:r}){let o=!0;return L(t.pipe(m(n=>n.closest("details:not([open])")),g(n=>e===n),m(()=>({action:"open",reveal:!0}))),r.pipe(g(n=>n||!o),O(()=>o=e.open),m(n=>({action:n?"open":"close"}))))}function Bn(e,t){return H(()=>{let r=new T;return r.subscribe(({action:o,reveal:n})=>{e.toggleAttribute("open",o==="open"),n&&e.scrollIntoView()}),ts(e,t).pipe(O(o=>r.next(o)),A(()=>r.complete()),m(o=>P({ref:e},o)))})}var Gn=0;function rs(e){let t=document.createElement("h3");t.innerHTML=e.innerHTML;let r=[t],o=e.nextElementSibling;for(;o&&!(o instanceof HTMLHeadingElement);)r.push(o),o=o.nextElementSibling;return r}function os(e,t){for(let r of M("[href], [src]",e))for(let o of["href","src"]){let n=r.getAttribute(o);if(n&&!/^(?:[a-z]+:)?\/\//i.test(n)){r[o]=new URL(r.getAttribute(o),t).toString();break}}for(let r of M("[name^=__], [for]",e))for(let o of["id","for","name"]){let n=r.getAttribute(o);n&&r.setAttribute(o,`${n}$preview_${Gn}`)}return Gn++,$(e)}function Jn(e,t){let{sitemap$:r}=t;if(!(e instanceof HTMLAnchorElement))return y;if(!(V("navigation.instant.preview")||e.hasAttribute("data-preview")))return y;e.removeAttribute("title");let o=z([Ye(e),it(e)]).pipe(m(([i,s])=>i||s),Y(),g(i=>i));return rt([r,o]).pipe(b(([i])=>{let s=new URL(e.href);return s.search=s.hash="",i.has(`${s}`)?$(s):y}),b(i=>xr(i).pipe(b(s=>os(s,i)))),b(i=>{let s=e.hash?`article [id="${e.hash.slice(1)}"]`:"article h1",a=ue(s,i);return typeof a=="undefined"?y:$(rs(a))})).pipe(b(i=>{let s=new F(a=>{let c=wr(...i);return a.next(c),document.body.append(c),()=>c.remove()});return Vt(e,P({content$:s},t))}))}var Xn=".node circle,.node ellipse,.node path,.node polygon,.node rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}marker{fill:var(--md-mermaid-edge-color)!important}.edgeLabel .label rect{fill:#0000}.flowchartTitleText{fill:var(--md-mermaid-label-fg-color)}.label{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.label foreignObject{line-height:normal;overflow:visible}.label div .edgeLabel{color:var(--md-mermaid-label-fg-color)}.edgeLabel,.edgeLabel p,.label div .edgeLabel{background-color:var(--md-mermaid-label-bg-color)}.edgeLabel,.edgeLabel p{fill:var(--md-mermaid-label-bg-color);color:var(--md-mermaid-edge-color)}.edgePath .path,.flowchart-link{stroke:var(--md-mermaid-edge-color)}.edgePath .arrowheadPath{fill:var(--md-mermaid-edge-color);stroke:none}.cluster rect{fill:var(--md-default-fg-color--lightest);stroke:var(--md-default-fg-color--lighter)}.cluster span{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}g #flowchart-circleEnd,g #flowchart-circleStart,g #flowchart-crossEnd,g #flowchart-crossStart,g #flowchart-pointEnd,g #flowchart-pointStart{stroke:none}.classDiagramTitleText{fill:var(--md-mermaid-label-fg-color)}g.classGroup line,g.classGroup rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}g.classGroup text{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.classLabel .box{fill:var(--md-mermaid-label-bg-color);background-color:var(--md-mermaid-label-bg-color);opacity:1}.classLabel .label{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.node .divider{stroke:var(--md-mermaid-node-fg-color)}.relation{stroke:var(--md-mermaid-edge-color)}.cardinality{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.cardinality text{fill:inherit!important}defs marker.marker.composition.class path,defs marker.marker.dependency.class path,defs marker.marker.extension.class path{fill:var(--md-mermaid-edge-color)!important;stroke:var(--md-mermaid-edge-color)!important}defs marker.marker.aggregation.class path{fill:var(--md-mermaid-label-bg-color)!important;stroke:var(--md-mermaid-edge-color)!important}.statediagramTitleText{fill:var(--md-mermaid-label-fg-color)}g.stateGroup rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}g.stateGroup .state-title{fill:var(--md-mermaid-label-fg-color)!important;font-family:var(--md-mermaid-font-family)}g.stateGroup .composit{fill:var(--md-mermaid-label-bg-color)}.nodeLabel,.nodeLabel p{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}a .nodeLabel{text-decoration:underline}.node circle.state-end,.node circle.state-start,.start-state{fill:var(--md-mermaid-edge-color);stroke:none}.end-state-inner,.end-state-outer{fill:var(--md-mermaid-edge-color)}.end-state-inner,.node circle.state-end{stroke:var(--md-mermaid-label-bg-color)}.transition{stroke:var(--md-mermaid-edge-color)}[id^=state-fork] rect,[id^=state-join] rect{fill:var(--md-mermaid-edge-color)!important;stroke:none!important}.statediagram-cluster.statediagram-cluster .inner{fill:var(--md-default-bg-color)}.statediagram-cluster rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}.statediagram-state rect.divider{fill:var(--md-default-fg-color--lightest);stroke:var(--md-default-fg-color--lighter)}defs #statediagram-barbEnd{stroke:var(--md-mermaid-edge-color)}[id^=entity] path,[id^=entity] rect{fill:var(--md-default-bg-color)}.relationshipLine{stroke:var(--md-mermaid-edge-color)}defs .marker.oneOrMore.er *,defs .marker.onlyOne.er *,defs .marker.zeroOrMore.er *,defs .marker.zeroOrOne.er *{stroke:var(--md-mermaid-edge-color)!important}text:not([class]):last-child{fill:var(--md-mermaid-label-fg-color)}.actor{fill:var(--md-mermaid-sequence-actor-bg-color);stroke:var(--md-mermaid-sequence-actor-border-color)}text.actor>tspan{fill:var(--md-mermaid-sequence-actor-fg-color);font-family:var(--md-mermaid-font-family)}line{stroke:var(--md-mermaid-sequence-actor-line-color)}.actor-man circle,.actor-man line{fill:var(--md-mermaid-sequence-actorman-bg-color);stroke:var(--md-mermaid-sequence-actorman-line-color)}.messageLine0,.messageLine1{stroke:var(--md-mermaid-sequence-message-line-color)}.note{fill:var(--md-mermaid-sequence-note-bg-color);stroke:var(--md-mermaid-sequence-note-border-color)}.loopText,.loopText>tspan,.messageText,.noteText>tspan{stroke:none;font-family:var(--md-mermaid-font-family)!important}.messageText{fill:var(--md-mermaid-sequence-message-fg-color)}.loopText,.loopText>tspan{fill:var(--md-mermaid-sequence-loop-fg-color)}.noteText>tspan{fill:var(--md-mermaid-sequence-note-fg-color)}#arrowhead path{fill:var(--md-mermaid-sequence-message-line-color);stroke:none}.loopLine{fill:var(--md-mermaid-sequence-loop-bg-color);stroke:var(--md-mermaid-sequence-loop-border-color)}.labelBox{fill:var(--md-mermaid-sequence-label-bg-color);stroke:none}.labelText,.labelText>span{fill:var(--md-mermaid-sequence-label-fg-color);font-family:var(--md-mermaid-font-family)}.sequenceNumber{fill:var(--md-mermaid-sequence-number-fg-color)}rect.rect{fill:var(--md-mermaid-sequence-box-bg-color);stroke:none}rect.rect+text.text{fill:var(--md-mermaid-sequence-box-fg-color)}defs #sequencenumber{fill:var(--md-mermaid-sequence-number-bg-color)!important}";var so,is=0;function as(){return typeof mermaid=="undefined"||mermaid instanceof Element?_t("https://unpkg.com/mermaid@11/dist/mermaid.min.js"):$(void 0)}function Zn(e){return e.classList.remove("mermaid"),so||(so=as().pipe(O(()=>mermaid.initialize({startOnLoad:!1,themeCSS:Xn,sequence:{actorFontSize:"16px",messageFontSize:"16px",noteFontSize:"16px"}})),m(()=>{}),Z(1))),so.subscribe(()=>go(null,null,function*(){e.classList.add("mermaid");let t=`__mermaid_${is++}`,r=x("div",{class:"mermaid"}),o=e.textContent,{svg:n,fn:i}=yield mermaid.render(t,o),s=r.attachShadow({mode:"closed"});s.innerHTML=n,e.replaceWith(r),i==null||i(s)})),so.pipe(m(()=>({ref:e})))}var ei=x("table");function ti(e){return e.replaceWith(ei),ei.replaceWith(Un(e)),$({ref:e})}function ss(e){let t=e.find(r=>r.checked)||e[0];return L(...e.map(r=>h(r,"change").pipe(m(()=>j(`label[for="${r.id}"]`))))).pipe(Q(j(`label[for="${t.id}"]`)),m(r=>({active:r})))}function ri(e,{viewport$:t,target$:r}){let o=j(".tabbed-labels",e),n=M(":scope > input",e),i=no("prev");e.append(i);let s=no("next");return e.append(s),H(()=>{let a=new T,c=a.pipe(oe(),ae(!0));z([a,Le(e),mt(e)]).pipe(W(c),$e(1,ye)).subscribe({next([{active:p},l]){let f=Be(p),{width:u}=de(p);e.style.setProperty("--md-indicator-x",`${f.x}px`),e.style.setProperty("--md-indicator-width",`${u}px`);let d=gr(o);(f.xd.x+l.width)&&o.scrollTo({left:Math.max(0,f.x-16),behavior:"smooth"})},complete(){e.style.removeProperty("--md-indicator-x"),e.style.removeProperty("--md-indicator-width")}}),z([Ge(o),Le(o)]).pipe(W(c)).subscribe(([p,l])=>{let f=At(o);i.hidden=p.x<16,s.hidden=p.x>f.width-l.width-16}),L(h(i,"click").pipe(m(()=>-1)),h(s,"click").pipe(m(()=>1))).pipe(W(c)).subscribe(p=>{let{width:l}=de(o);o.scrollBy({left:l*p,behavior:"smooth"})}),r.pipe(W(c),g(p=>n.includes(p))).subscribe(p=>p.click()),o.classList.add("tabbed-labels--linked");for(let p of n){let l=j(`label[for="${p.id}"]`);l.replaceChildren(x("a",{href:`#${l.htmlFor}`,tabIndex:-1},...Array.from(l.childNodes))),h(l.firstElementChild,"click").pipe(W(c),g(f=>!(f.metaKey||f.ctrlKey)),O(f=>{f.preventDefault(),f.stopPropagation()})).subscribe(()=>{history.replaceState({},"",`#${l.htmlFor}`),l.click()})}return V("content.tabs.link")&&a.pipe(Ie(1),te(t)).subscribe(([{active:p},{offset:l}])=>{let f=p.innerText.trim();if(p.hasAttribute("data-md-switching"))p.removeAttribute("data-md-switching");else{let u=e.offsetTop-l.y;for(let v of M("[data-tabs]"))for(let S of M(":scope > input",v)){let X=j(`label[for="${S.id}"]`);if(X!==p&&X.innerText.trim()===f){X.setAttribute("data-md-switching",""),S.click();break}}window.scrollTo({top:e.offsetTop-u});let d=__md_get("__tabs")||[];__md_set("__tabs",[...new Set([f,...d])])}}),a.pipe(W(c)).subscribe(()=>{for(let p of M("audio, video",e))p.offsetWidth&&p.autoplay?p.play().catch(()=>{}):p.pause()}),ss(n).pipe(O(p=>a.next(p)),A(()=>a.complete()),m(p=>P({ref:e},p)))}).pipe(et(pe))}function oi(e,t){let{viewport$:r,target$:o,print$:n}=t;return L(...M(".annotate:not(.highlight)",e).map(i=>zn(i,{target$:o,print$:n})),...M("pre:not(.mermaid) > code",e).map(i=>Yn(i,{target$:o,print$:n})),...M("a",e).map(i=>Jn(i,t)),...M("pre.mermaid",e).map(i=>Zn(i)),...M("table:not([class])",e).map(i=>ti(i)),...M("details",e).map(i=>Bn(i,{target$:o,print$:n})),...M("[data-tabs]",e).map(i=>ri(i,{viewport$:r,target$:o})),...M("[title]:not([data-preview])",e).filter(()=>V("content.tooltips")).map(i=>Xe(i,{viewport$:r})),...M(".footnote-ref",e).filter(()=>V("content.footnote.tooltips")).map(i=>Vt(i,{content$:new F(s=>{let a=new URL(i.href).hash.slice(1),c=Array.from(document.getElementById(a).cloneNode(!0).children),p=wr(...c);return s.next(p),document.body.append(p),()=>p.remove()}),viewport$:r})))}function cs(e,{alert$:t}){return t.pipe(b(r=>L($(!0),$(!1).pipe(nt(2e3))).pipe(m(o=>({message:r,active:o})))))}function ni(e,t){let r=j(".md-typeset",e);return H(()=>{let o=new T;return o.subscribe(({message:n,active:i})=>{e.classList.toggle("md-dialog--active",i),r.textContent=n}),cs(e,t).pipe(O(n=>o.next(n)),A(()=>o.complete()),m(n=>P({ref:e},n)))})}var ps=0;function ls(e,t){document.body.append(e);let{width:r}=de(e);e.style.setProperty("--md-tooltip-width",`${r}px`),e.remove();let o=vr(t),n=typeof o!="undefined"?Ge(o):$({x:0,y:0}),i=L(Ye(t),it(t)).pipe(Y());return z([i,n]).pipe(m(([s,a])=>{let{x:c,y:p}=Be(t),l=de(t),f=t.closest("table");return f&&t.parentElement&&(c+=f.offsetLeft+t.parentElement.offsetLeft,p+=f.offsetTop+t.parentElement.offsetTop),{active:s,offset:{x:c-a.x+l.width/2-r/2,y:p-a.y+l.height+8}}}))}function ii(e){let t=e.title;if(!t.length)return y;let r=`__tooltip_${ps++}`,o=Dt(r,"inline"),n=j(".md-typeset",o);return n.innerHTML=t,H(()=>{let i=new T;return i.subscribe({next({offset:s}){o.style.setProperty("--md-tooltip-x",`${s.x}px`),o.style.setProperty("--md-tooltip-y",`${s.y}px`)},complete(){o.style.removeProperty("--md-tooltip-x"),o.style.removeProperty("--md-tooltip-y")}}),L(i.pipe(g(({active:s})=>s)),i.pipe(Ae(250),g(({active:s})=>!s))).subscribe({next({active:s}){s?(e.insertAdjacentElement("afterend",o),e.setAttribute("aria-describedby",r),e.removeAttribute("title")):(o.remove(),e.removeAttribute("aria-describedby"),e.setAttribute("title",t))},complete(){o.remove(),e.removeAttribute("aria-describedby"),e.setAttribute("title",t)}}),i.pipe($e(16,ye)).subscribe(({active:s})=>{o.classList.toggle("md-tooltip--active",s)}),i.pipe(gt(125,ye),g(()=>!!e.offsetParent),m(()=>e.offsetParent.getBoundingClientRect()),m(({x:s})=>s)).subscribe({next(s){s?o.style.setProperty("--md-tooltip-0",`${-s}px`):o.style.removeProperty("--md-tooltip-0")},complete(){o.style.removeProperty("--md-tooltip-0")}}),ls(o,e).pipe(O(s=>i.next(s)),A(()=>i.complete()),m(s=>P({ref:e},s)))}).pipe(et(pe))}function ms({viewport$:e}){if(!V("header.autohide"))return $(!1);let t=e.pipe(m(({offset:{y:n}})=>n),ot(2,1),m(([n,i])=>[nMath.abs(i-n.y)>100),m(([,[n]])=>n),Y()),o=Je("search");return z([e,o]).pipe(m(([{offset:n},i])=>n.y>400&&!i),Y(),b(n=>n?r:$(!1)),Q(!1))}function ai(e,t){return H(()=>z([Le(e),ms(t)])).pipe(m(([{height:r},o])=>({height:r,hidden:o})),Y((r,o)=>r.height===o.height&&r.hidden===o.hidden),Z(1))}function si(e,{header$:t,main$:r}){return H(()=>{let o=new T,n=o.pipe(oe(),ae(!0));o.pipe(ne("active"),Pe(t)).subscribe(([{active:s},{hidden:a}])=>{e.classList.toggle("md-header--shadow",s&&!a),e.hidden=a});let i=fe(M("[title]",e)).pipe(g(()=>V("content.tooltips")),J(s=>ii(s)));return r.subscribe(o),t.pipe(W(n),m(s=>P({ref:e},s)),Ve(i.pipe(W(n))))})}function fs(e,{viewport$:t,header$:r}){return Er(e,{viewport$:t,header$:r}).pipe(m(({offset:{y:o}})=>{let{height:n}=de(e);return{active:n>0&&o>=n}}),ne("active"))}function ci(e,t){return H(()=>{let r=new T;r.subscribe({next({active:n}){e.classList.toggle("md-header__title--active",n)},complete(){e.classList.remove("md-header__title--active")}});let o=ue(".md-content h1");return typeof o=="undefined"?y:fs(o,t).pipe(O(n=>r.next(n)),A(()=>r.complete()),m(n=>P({ref:e},n)))})}function pi(e,{viewport$:t,header$:r}){let o=r.pipe(m(({height:i})=>i),Y()),n=o.pipe(b(()=>Le(e).pipe(m(({height:i})=>({top:e.offsetTop,bottom:e.offsetTop+i})),ne("bottom"))));return z([o,n,t]).pipe(m(([i,{top:s,bottom:a},{offset:{y:c},size:{height:p}}])=>(p=Math.max(0,p-Math.max(0,s-c,i)-Math.max(0,p+c-a)),{offset:s-i,height:p,active:s-i<=c})),Y((i,s)=>i.offset===s.offset&&i.height===s.height&&i.active===s.active))}function us(e){let t=__md_get("__palette")||{index:e.findIndex(o=>matchMedia(o.getAttribute("data-md-color-media")).matches)},r=Math.max(0,Math.min(t.index,e.length-1));return $(...e).pipe(J(o=>h(o,"change").pipe(m(()=>o))),Q(e[r]),m(o=>({index:e.indexOf(o),color:{media:o.getAttribute("data-md-color-media"),scheme:o.getAttribute("data-md-color-scheme"),primary:o.getAttribute("data-md-color-primary"),accent:o.getAttribute("data-md-color-accent")}})),Z(1))}function li(e){let t=M("input",e),r=x("meta",{name:"theme-color"});document.head.appendChild(r);let o=x("meta",{name:"color-scheme"});document.head.appendChild(o);let n=Wt("(prefers-color-scheme: light)");return H(()=>{let i=new T;return i.subscribe(s=>{if(document.body.setAttribute("data-md-color-switching",""),s.color.media==="(prefers-color-scheme)"){let a=matchMedia("(prefers-color-scheme: light)"),c=document.querySelector(a.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");s.color.scheme=c.getAttribute("data-md-color-scheme"),s.color.primary=c.getAttribute("data-md-color-primary"),s.color.accent=c.getAttribute("data-md-color-accent")}for(let[a,c]of Object.entries(s.color))document.body.setAttribute(`data-md-color-${a}`,c);for(let a=0;as.key==="Enter"),te(i,(s,a)=>a)).subscribe(({index:s})=>{s=(s+1)%t.length,t[s].click(),t[s].focus()}),i.pipe(m(()=>{let s=Ce("header"),a=window.getComputedStyle(s);return o.content=a.colorScheme,a.backgroundColor.match(/\d+/g).map(c=>(+c).toString(16).padStart(2,"0")).join("")})).subscribe(s=>r.content=`#${s}`),i.pipe(xe(pe)).subscribe(()=>{document.body.removeAttribute("data-md-color-switching")}),us(t).pipe(W(n.pipe(Ie(1))),vt(),O(s=>i.next(s)),A(()=>i.complete()),m(s=>P({ref:e},s)))})}function mi(e,{progress$:t}){return H(()=>{let r=new T;return r.subscribe(({value:o})=>{e.style.setProperty("--md-progress-value",`${o}`)}),t.pipe(O(o=>r.next({value:o})),A(()=>r.complete()),m(o=>({ref:e,value:o})))})}function fi(e,t){return e.protocol=t.protocol,e.hostname=t.hostname,e}function ds(e,t){let r=new Map;for(let o of M("url",e)){let n=j("loc",o),i=[fi(new URL(n.textContent),t)];r.set(`${i[0]}`,i);for(let s of M("[rel=alternate]",o)){let a=s.getAttribute("href");a!=null&&i.push(fi(new URL(a),t))}}return r}function kt(e){return En(new URL("sitemap.xml",e)).pipe(m(t=>ds(t,new URL(e))),ve(()=>$(new Map)),le())}function ui({document$:e}){let t=new Map;e.pipe(b(()=>M("link[rel=alternate]")),m(r=>new URL(r.href)),g(r=>!t.has(r.toString())),J(r=>kt(r).pipe(m(o=>[r,o]),ve(()=>y)))).subscribe(([r,o])=>{t.set(r.toString().replace(/\/$/,""),o)}),h(document.body,"click").pipe(g(r=>!r.metaKey&&!r.ctrlKey),b(r=>{if(r.target instanceof Element){let o=r.target.closest("a");if(o&&!o.target){let n=[...t].find(([f])=>o.href.startsWith(`${f}/`));if(typeof n=="undefined")return y;let[i,s]=n,a=we();if(a.href.startsWith(i))return y;let c=Te(),p=a.href.replace(c.base,"");p=`${i}/${p}`;let l=s.has(p.split("#")[0])?new URL(p,c.base):new URL(i);return r.preventDefault(),$(l)}}return y})).subscribe(r=>st(r,!0))}var co=$t(ao());function hs(e){e.setAttribute("data-md-copying","");let t=e.closest("[data-copy]"),r=t?t.getAttribute("data-copy"):e.innerText;return e.removeAttribute("data-md-copying"),r.trimEnd()}function di({alert$:e}){co.default.isSupported()&&new F(t=>{new co.default("[data-clipboard-target], [data-clipboard-text]",{text:r=>r.getAttribute("data-clipboard-text")||hs(j(r.getAttribute("data-clipboard-target")))}).on("success",r=>t.next(r))}).pipe(O(t=>{t.trigger.focus()}),m(()=>Me("clipboard.copied"))).subscribe(e)}function hi(e,t){if(!(e.target instanceof Element))return y;let r=e.target.closest("a");if(r===null)return y;if(r.target||e.metaKey||e.ctrlKey)return y;let o=new URL(r.href);return o.search=o.hash="",t.has(`${o}`)?(e.preventDefault(),$(r)):y}function bi(e){let t=new Map;for(let r of M(":scope > *",e.head))t.set(r.outerHTML,r);return t}function vi(e){for(let t of M("[href], [src]",e))for(let r of["href","src"]){let o=t.getAttribute(r);if(o&&!/^(?:[a-z]+:)?\/\//i.test(o)){t[r]=t[r];break}}return $(e)}function bs(e){for(let o of["[data-md-component=announce]","[data-md-component=container]","[data-md-component=header-topic]","[data-md-component=outdated]","[data-md-component=logo]","[data-md-component=skip]",...V("navigation.tabs.sticky")?["[data-md-component=tabs]"]:[]]){let n=ue(o),i=ue(o,e);typeof n!="undefined"&&typeof i!="undefined"&&n.replaceWith(i)}let t=bi(document);for(let[o,n]of bi(e))t.has(o)?t.delete(o):document.head.appendChild(n);for(let o of t.values()){let n=o.getAttribute("name");n!=="theme-color"&&n!=="color-scheme"&&o.remove()}let r=Ce("container");return Ke(M("script",r)).pipe(b(o=>{let n=e.createElement("script");if(o.src){for(let i of o.getAttributeNames())n.setAttribute(i,o.getAttribute(i));return o.replaceWith(n),new F(i=>{n.onload=()=>i.complete()})}else return n.textContent=o.textContent,o.replaceWith(n),y}),oe(),ae(document))}function gi({sitemap$:e,location$:t,viewport$:r,progress$:o}){if(location.protocol==="file:")return y;$(document).subscribe(vi);let n=h(document.body,"click").pipe(Pe(e),b(([a,c])=>hi(a,c)),m(({href:a})=>new URL(a)),le()),i=h(window,"popstate").pipe(m(we),le());n.pipe(te(r)).subscribe(([a,{offset:c}])=>{history.replaceState(c,""),history.pushState(null,"",a)}),L(n,i).subscribe(t);let s=t.pipe(ne("pathname"),b(a=>xr(a,{progress$:o}).pipe(ve(()=>(st(a,!0),y)))),b(vi),b(bs),le());return L(s.pipe(te(t,(a,c)=>c)),s.pipe(b(()=>t),ne("hash")),t.pipe(Y((a,c)=>a.pathname===c.pathname&&a.hash===c.hash),b(()=>n),O(()=>history.back()))).subscribe(a=>{var c,p;history.state!==null||!a.hash?window.scrollTo(0,(p=(c=history.state)==null?void 0:c.y)!=null?p:0):(history.scrollRestoration="auto",gn(a.hash),history.scrollRestoration="manual")}),t.subscribe(()=>{history.scrollRestoration="manual"}),h(window,"beforeunload").subscribe(()=>{history.scrollRestoration="auto"}),r.pipe(ne("offset"),Ae(100)).subscribe(({offset:a})=>{history.replaceState(a,"")}),V("navigation.instant.prefetch")&&L(h(document.body,"mousemove"),h(document.body,"focusin")).pipe(Pe(e),b(([a,c])=>hi(a,c)),Ae(25),Qr(({href:a})=>a),hr(a=>{let c=document.createElement("link");return c.rel="prefetch",c.href=a.toString(),document.head.appendChild(c),h(c,"load").pipe(m(()=>c),Ee(1))})).subscribe(a=>a.remove()),s}var yi=$t(ro());function xi(e){let t=e.separator.split("|").map(n=>n.replace(/(\(\?[!=<][^)]+\))/g,"").length===0?"\uFFFD":n).join("|"),r=new RegExp(t,"img"),o=(n,i,s)=>`${i}${s}`;return n=>{n=n.replace(/[\s*+\-:~^]+/g," ").replace(/&/g,"&").trim();let i=new RegExp(`(^|${e.separator}|)(${n.replace(/[|\\{}()[\]^$+*?.-]/g,"\\$&").replace(r,"|")})`,"img");return s=>(0,yi.default)(s).replace(i,o).replace(/<\/mark>(\s+)]*>/img,"$1")}}function zt(e){return e.type===1}function Sr(e){return e.type===3}function Ei(e,t){let r=Mn(e);return L($(location.protocol!=="file:"),Je("search")).pipe(Re(o=>o),b(()=>t)).subscribe(({config:o,docs:n})=>r.next({type:0,data:{config:o,docs:n,options:{suggest:V("search.suggest")}}})),r}function wi(e){var l;let{selectedVersionSitemap:t,selectedVersionBaseURL:r,currentLocation:o,currentBaseURL:n}=e,i=(l=po(n))==null?void 0:l.pathname;if(i===void 0)return;let s=ys(o.pathname,i);if(s===void 0)return;let a=Es(t.keys());if(!t.has(a))return;let c=po(s,a);if(!c||!t.has(c.href))return;let p=po(s,r);if(p)return p.hash=o.hash,p.search=o.search,p}function po(e,t){try{return new URL(e,t)}catch(r){return}}function ys(e,t){if(e.startsWith(t))return e.slice(t.length)}function xs(e,t){let r=Math.min(e.length,t.length),o;for(o=0;oy)),o=r.pipe(m(n=>{let[,i]=t.base.match(/([^/]+)\/?$/);return n.find(({version:s,aliases:a})=>s===i||a.includes(i))||n[0]}));r.pipe(m(n=>new Map(n.map(i=>[`${new URL(`../${i.version}/`,t.base)}`,i]))),b(n=>h(document.body,"click").pipe(g(i=>!i.metaKey&&!i.ctrlKey),te(o),b(([i,s])=>{if(i.target instanceof Element){let a=i.target.closest("a");if(a&&!a.target&&n.has(a.href)){let c=a.href;return!i.target.closest(".md-version")&&n.get(c)===s?y:(i.preventDefault(),$(new URL(c)))}}return y}),b(i=>kt(i).pipe(m(s=>{var a;return(a=wi({selectedVersionSitemap:s,selectedVersionBaseURL:i,currentLocation:we(),currentBaseURL:t.base}))!=null?a:i})))))).subscribe(n=>st(n,!0)),z([r,o]).subscribe(([n,i])=>{j(".md-header__topic").appendChild(Wn(n,i))}),e.pipe(b(()=>o)).subscribe(n=>{var a;let i=new URL(t.base),s=__md_get("__outdated",sessionStorage,i);if(s===null){s=!0;let c=((a=t.version)==null?void 0:a.default)||"latest";Array.isArray(c)||(c=[c]);e:for(let p of c)for(let l of n.aliases.concat(n.version))if(new RegExp(p,"i").test(l)){s=!1;break e}__md_set("__outdated",s,sessionStorage,i)}if(s)for(let c of me("outdated"))c.hidden=!1})}function ws(e,{worker$:t}){let{searchParams:r}=we();r.has("q")&&(at("search",!0),e.value=r.get("q"),e.focus(),Je("search").pipe(Re(i=>!i)).subscribe(()=>{let i=we();i.searchParams.delete("q"),history.replaceState({},"",`${i}`)}));let o=Ye(e),n=L(t.pipe(Re(zt)),h(e,"keyup"),o).pipe(m(()=>e.value),Y());return z([n,o]).pipe(m(([i,s])=>({value:i,focus:s})),Z(1))}function Si(e,{worker$:t}){let r=new T,o=r.pipe(oe(),ae(!0));z([t.pipe(Re(zt)),r],(i,s)=>s).pipe(ne("value")).subscribe(({value:i})=>t.next({type:2,data:i})),r.pipe(ne("focus")).subscribe(({focus:i})=>{i&&at("search",i)}),h(e.form,"reset").pipe(W(o)).subscribe(()=>e.focus());let n=j("header [for=__search]");return h(n,"click").subscribe(()=>e.focus()),ws(e,{worker$:t}).pipe(O(i=>r.next(i)),A(()=>r.complete()),m(i=>P({ref:e},i)),Z(1))}function Oi(e,{worker$:t,query$:r}){let o=new T,n=un(e.parentElement).pipe(g(Boolean)),i=e.parentElement,s=j(":scope > :first-child",e),a=j(":scope > :last-child",e);Je("search").subscribe(l=>{a.setAttribute("role",l?"list":"presentation"),a.hidden=!l}),o.pipe(te(r),Gr(t.pipe(Re(zt)))).subscribe(([{items:l},{value:f}])=>{switch(l.length){case 0:s.textContent=f.length?Me("search.result.none"):Me("search.result.placeholder");break;case 1:s.textContent=Me("search.result.one");break;default:let u=br(l.length);s.textContent=Me("search.result.other",u)}});let c=o.pipe(O(()=>a.innerHTML=""),b(({items:l})=>L($(...l.slice(0,10)),$(...l.slice(10)).pipe(ot(4),Xr(n),b(([f])=>f)))),m(Fn),le());return c.subscribe(l=>a.appendChild(l)),c.pipe(J(l=>{let f=ue("details",l);return typeof f=="undefined"?y:h(f,"toggle").pipe(W(o),m(()=>f))})).subscribe(l=>{l.open===!1&&l.offsetTop<=i.scrollTop&&i.scrollTo({top:l.offsetTop})}),t.pipe(g(Sr),m(({data:l})=>l)).pipe(O(l=>o.next(l)),A(()=>o.complete()),m(l=>P({ref:e},l)))}function Ts(e,{query$:t}){return t.pipe(m(({value:r})=>{let o=we();return o.hash="",r=r.replace(/\s+/g,"+").replace(/&/g,"%26").replace(/=/g,"%3D"),o.search=`q=${r}`,{url:o}}))}function Li(e,t){let r=new T,o=r.pipe(oe(),ae(!0));return r.subscribe(({url:n})=>{e.setAttribute("data-clipboard-text",e.href),e.href=`${n}`}),h(e,"click").pipe(W(o)).subscribe(n=>n.preventDefault()),Ts(e,t).pipe(O(n=>r.next(n)),A(()=>r.complete()),m(n=>P({ref:e},n)))}function Mi(e,{worker$:t,keyboard$:r}){let o=new T,n=Ce("search-query"),i=L(h(n,"keydown"),h(n,"focus")).pipe(xe(pe),m(()=>n.value),Y());return o.pipe(Pe(i),m(([{suggest:a},c])=>{let p=c.split(/([\s-]+)/);if(a!=null&&a.length&&p[p.length-1]){let l=a[a.length-1];l.startsWith(p[p.length-1])&&(p[p.length-1]=l)}else p.length=0;return p})).subscribe(a=>e.innerHTML=a.join("").replace(/\s/g," ")),r.pipe(g(({mode:a})=>a==="search")).subscribe(a=>{a.type==="ArrowRight"&&e.innerText.length&&n.selectionStart===n.value.length&&(n.value=e.innerText)}),t.pipe(g(Sr),m(({data:a})=>a)).pipe(O(a=>o.next(a)),A(()=>o.complete()),m(()=>({ref:e})))}function _i(e,{index$:t,keyboard$:r}){let o=Te();try{let n=Ei(o.search,t),i=Ce("search-query",e),s=Ce("search-result",e);h(e,"click").pipe(g(({target:c})=>c instanceof Element&&!!c.closest("a"))).subscribe(()=>at("search",!1)),r.pipe(g(({mode:c})=>c==="search")).subscribe(c=>{let p=Ne();switch(c.type){case"Enter":if(p===i){let l=new Map;for(let f of M(":first-child [href]",s)){let u=f.firstElementChild;l.set(f,parseFloat(u.getAttribute("data-md-score")))}if(l.size){let[[f]]=[...l].sort(([,u],[,d])=>d-u);f.click()}c.claim()}break;case"Escape":case"Tab":at("search",!1),i.blur();break;case"ArrowUp":case"ArrowDown":if(typeof p=="undefined")i.focus();else{let l=[i,...M(":not(details) > [href], summary, details[open] [href]",s)],f=Math.max(0,(Math.max(0,l.indexOf(p))+l.length+(c.type==="ArrowUp"?-1:1))%l.length);l[f].focus()}c.claim();break;default:i!==Ne()&&i.focus()}}),r.pipe(g(({mode:c})=>c==="global")).subscribe(c=>{switch(c.type){case"f":case"s":case"/":i.focus(),i.select(),c.claim();break}});let a=Si(i,{worker$:n});return L(a,Oi(s,{worker$:n,query$:a})).pipe(Ve(...me("search-share",e).map(c=>Li(c,{query$:a})),...me("search-suggest",e).map(c=>Mi(c,{worker$:n,keyboard$:r}))))}catch(n){return e.hidden=!0,tt}}function Ai(e,{index$:t,location$:r}){return z([t,r.pipe(Q(we()),g(o=>!!o.searchParams.get("h")))]).pipe(m(([o,n])=>xi(o.config)(n.searchParams.get("h"))),m(o=>{var s;let n=new Map,i=document.createNodeIterator(e,NodeFilter.SHOW_TEXT);for(let a=i.nextNode();a;a=i.nextNode())if((s=a.parentElement)!=null&&s.offsetHeight){let c=a.textContent,p=o(c);p.length>c.length&&n.set(a,p)}for(let[a,c]of n){let{childNodes:p}=x("span",null,c);a.replaceWith(...Array.from(p))}return{ref:e,nodes:n}}))}function Ss(e,{viewport$:t,main$:r}){let o=e.closest(".md-grid"),n=o.offsetTop-o.parentElement.offsetTop;return z([r,t]).pipe(m(([{offset:i,height:s},{offset:{y:a}}])=>(s=s+Math.min(n,Math.max(0,a-i))-n,{height:s,locked:a>=i+n})),Y((i,s)=>i.height===s.height&&i.locked===s.locked))}function lo(e,o){var n=o,{header$:t}=n,r=vo(n,["header$"]);let i=j(".md-sidebar__scrollwrap",e),{y:s}=Be(i);return H(()=>{let a=new T,c=a.pipe(oe(),ae(!0)),p=a.pipe($e(0,ye));return p.pipe(te(t)).subscribe({next([{height:l},{height:f}]){i.style.height=`${l-2*s}px`,e.style.top=`${f}px`},complete(){i.style.height="",e.style.top=""}}),p.pipe(Re()).subscribe(()=>{for(let l of M(".md-nav__link--active[href]",e)){if(!l.clientHeight)continue;let f=l.closest(".md-sidebar__scrollwrap");if(typeof f!="undefined"){let u=l.offsetTop-f.offsetTop,{height:d}=de(f);f.scrollTo({top:u-d/2})}}}),fe(M("label[tabindex]",e)).pipe(J(l=>h(l,"click").pipe(xe(pe),m(()=>l),W(c)))).subscribe(l=>{let f=j(`[id="${l.htmlFor}"]`);j(`[aria-labelledby="${l.id}"]`).setAttribute("aria-expanded",`${f.checked}`)}),V("content.tooltips")&&fe(M("abbr[title]",e)).pipe(J(l=>Xe(l,{viewport$})),W(c)).subscribe(),Ss(e,r).pipe(O(l=>a.next(l)),A(()=>a.complete()),m(l=>P({ref:e},l)))})}function Ci(e,t){if(typeof t!="undefined"){let r=`https://api.github.com/repos/${e}/${t}`;return rt(ze(`${r}/releases/latest`).pipe(ve(()=>y),m(o=>({version:o.tag_name})),Qe({})),ze(r).pipe(ve(()=>y),m(o=>({stars:o.stargazers_count,forks:o.forks_count})),Qe({}))).pipe(m(([o,n])=>P(P({},o),n)))}else{let r=`https://api.github.com/users/${e}`;return ze(r).pipe(m(o=>({repositories:o.public_repos})),Qe({}))}}function ki(e,t){let r=`https://${e}/api/v4/projects/${encodeURIComponent(t)}`;return rt(ze(`${r}/releases/permalink/latest`).pipe(ve(()=>y),m(({tag_name:o})=>({version:o})),Qe({})),ze(r).pipe(ve(()=>y),m(({star_count:o,forks_count:n})=>({stars:o,forks:n})),Qe({}))).pipe(m(([o,n])=>P(P({},o),n)))}function Hi(e){let t=e.match(/^.+github\.com\/([^/]+)\/?([^/]+)?/i);if(t){let[,r,o]=t;return Ci(r,o)}if(t=e.match(/^.+?([^/]*gitlab[^/]+)\/(.+?)\/?$/i),t){let[,r,o]=t;return ki(r,o)}return y}var Os;function Ls(e){return Os||(Os=H(()=>{let t=__md_get("__source",sessionStorage);if(t)return $(t);if(me("consent").length){let o=__md_get("__consent");if(!(o&&o.github))return y}return Hi(e.href).pipe(O(o=>__md_set("__source",o,sessionStorage)))}).pipe(ve(()=>y),g(t=>Object.keys(t).length>0),m(t=>({facts:t})),Z(1)))}function $i(e){let t=j(":scope > :last-child",e);return H(()=>{let r=new T;return r.subscribe(({facts:o})=>{t.appendChild(jn(o)),t.classList.add("md-source__repository--active")}),Ls(e).pipe(O(o=>r.next(o)),A(()=>r.complete()),m(o=>P({ref:e},o)))})}function Ms(e,{viewport$:t,header$:r}){return Le(document.body).pipe(b(()=>Er(e,{header$:r,viewport$:t})),m(({offset:{y:o}})=>({hidden:o>=10})),ne("hidden"))}function Pi(e,t){return H(()=>{let r=new T;return r.subscribe({next({hidden:o}){e.hidden=o},complete(){e.hidden=!1}}),(V("navigation.tabs.sticky")?$({hidden:!1}):Ms(e,t)).pipe(O(o=>r.next(o)),A(()=>r.complete()),m(o=>P({ref:e},o)))})}function _s(e,{viewport$:t,header$:r}){let o=new Map,n=M(".md-nav__link",e);for(let a of n){let c=decodeURIComponent(a.hash.substring(1)),p=ue(`[id="${c}"]`);typeof p!="undefined"&&o.set(a,p)}let i=r.pipe(ne("height"),m(({height:a})=>{let c=Ce("main"),p=j(":scope > :first-child",c);return a+.8*(p.offsetTop-c.offsetTop)}),le());return Le(document.body).pipe(ne("height"),b(a=>H(()=>{let c=[];return $([...o].reduce((p,[l,f])=>{for(;c.length&&o.get(c[c.length-1]).tagName>=f.tagName;)c.pop();let u=f.offsetTop;for(;!u&&f.parentElement;)f=f.parentElement,u=f.offsetTop;let d=f.offsetParent;for(;d;d=d.offsetParent)u+=d.offsetTop;return p.set([...c=[...c,l]].reverse(),u)},new Map))}).pipe(m(c=>new Map([...c].sort(([,p],[,l])=>p-l))),Pe(i),b(([c,p])=>t.pipe(Ut(([l,f],{offset:{y:u},size:d})=>{let v=u+d.height>=Math.floor(a.height);for(;f.length;){let[,S]=f[0];if(S-p=u&&!v)f=[l.pop(),...f];else break}return[l,f]},[[],[...c]]),Y((l,f)=>l[0]===f[0]&&l[1]===f[1])))))).pipe(m(([a,c])=>({prev:a.map(([p])=>p),next:c.map(([p])=>p)})),Q({prev:[],next:[]}),ot(2,1),m(([a,c])=>a.prev.length{let i=new T,s=i.pipe(oe(),ae(!0));if(i.subscribe(({prev:a,next:c})=>{for(let[p]of c)p.classList.remove("md-nav__link--passed"),p.classList.remove("md-nav__link--active");for(let[p,[l]]of a.entries())l.classList.add("md-nav__link--passed"),l.classList.toggle("md-nav__link--active",p===a.length-1)}),V("toc.follow")){let a=L(t.pipe(Ae(1),m(()=>{})),t.pipe(Ae(250),m(()=>"smooth")));i.pipe(g(({prev:c})=>c.length>0),Pe(o.pipe(xe(pe))),te(a)).subscribe(([[{prev:c}],p])=>{let[l]=c[c.length-1];if(l.offsetHeight){let f=vr(l);if(typeof f!="undefined"){let u=l.offsetTop-f.offsetTop,{height:d}=de(f);f.scrollTo({top:u-d/2,behavior:p})}}})}return V("navigation.tracking")&&t.pipe(W(s),ne("offset"),Ae(250),Ie(1),W(n.pipe(Ie(1))),vt({delay:250}),te(i)).subscribe(([,{prev:a}])=>{let c=we(),p=a[a.length-1];if(p&&p.length){let[l]=p,{hash:f}=new URL(l.href);c.hash!==f&&(c.hash=f,history.replaceState({},"",`${c}`))}else c.hash="",history.replaceState({},"",`${c}`)}),_s(e,{viewport$:t,header$:r}).pipe(O(a=>i.next(a)),A(()=>i.complete()),m(a=>P({ref:e},a)))})}function As(e,{viewport$:t,main$:r,target$:o}){let n=t.pipe(m(({offset:{y:s}})=>s),ot(2,1),m(([s,a])=>s>a&&a>0),Y()),i=r.pipe(m(({active:s})=>s));return z([i,n]).pipe(m(([s,a])=>!(s&&a)),Y(),W(o.pipe(Ie(1))),ae(!0),vt({delay:250}),m(s=>({hidden:s})))}function Ii(e,{viewport$:t,header$:r,main$:o,target$:n}){let i=new T,s=i.pipe(oe(),ae(!0));return i.subscribe({next({hidden:a}){e.hidden=a,a?(e.setAttribute("tabindex","-1"),e.blur()):e.removeAttribute("tabindex")},complete(){e.style.top="",e.hidden=!0,e.removeAttribute("tabindex")}}),r.pipe(W(s),ne("height")).subscribe(({height:a})=>{e.style.top=`${a+16}px`}),h(e,"click").subscribe(a=>{a.preventDefault(),window.scrollTo({top:0})}),As(e,{viewport$:t,main$:o,target$:n}).pipe(O(a=>i.next(a)),A(()=>i.complete()),m(a=>P({ref:e},a)))}function Fi({document$:e,viewport$:t}){e.pipe(b(()=>M(".md-ellipsis")),J(r=>mt(r).pipe(W(e.pipe(Ie(1))),g(o=>o),m(()=>r),Ee(1))),g(r=>r.offsetWidth{let o=r.innerText,n=r.closest("a")||r;return n.title=o,V("content.tooltips")?Xe(n,{viewport$:t}).pipe(W(e.pipe(Ie(1))),A(()=>n.removeAttribute("title"))):y})).subscribe(),V("content.tooltips")&&e.pipe(b(()=>M(".md-status")),J(r=>Xe(r,{viewport$:t}))).subscribe()}function ji({document$:e,tablet$:t}){e.pipe(b(()=>M(".md-toggle--indeterminate")),O(r=>{r.indeterminate=!0,r.checked=!1}),J(r=>h(r,"change").pipe(Jr(()=>r.classList.contains("md-toggle--indeterminate")),m(()=>r))),te(t)).subscribe(([r,o])=>{r.classList.remove("md-toggle--indeterminate"),o&&(r.checked=!1)})}function Cs(){return/(iPad|iPhone|iPod)/.test(navigator.userAgent)}function Ui({document$:e}){e.pipe(b(()=>M("[data-md-scrollfix]")),O(t=>t.removeAttribute("data-md-scrollfix")),g(Cs),J(t=>h(t,"touchstart").pipe(m(()=>t)))).subscribe(t=>{let r=t.scrollTop;r===0?t.scrollTop=1:r+t.offsetHeight===t.scrollHeight&&(t.scrollTop=r-1)})}function Wi({viewport$:e,tablet$:t}){z([Je("search"),t]).pipe(m(([r,o])=>r&&!o),b(r=>$(r).pipe(nt(r?400:100))),te(e)).subscribe(([r,{offset:{y:o}}])=>{if(r)document.body.setAttribute("data-md-scrolllock",""),document.body.style.top=`-${o}px`;else{let n=-1*parseInt(document.body.style.top,10);document.body.removeAttribute("data-md-scrolllock"),document.body.style.top="",n&&window.scrollTo(0,n)}})}Object.entries||(Object.entries=function(e){let t=[];for(let r of Object.keys(e))t.push([r,e[r]]);return t});Object.values||(Object.values=function(e){let t=[];for(let r of Object.keys(e))t.push(e[r]);return t});typeof Element!="undefined"&&(Element.prototype.scrollTo||(Element.prototype.scrollTo=function(e,t){typeof e=="object"?(this.scrollLeft=e.left,this.scrollTop=e.top):(this.scrollLeft=e,this.scrollTop=t)}),Element.prototype.replaceWith||(Element.prototype.replaceWith=function(...e){let t=this.parentNode;if(t){e.length===0&&t.removeChild(this);for(let r=e.length-1;r>=0;r--){let o=e[r];typeof o=="string"?o=document.createTextNode(o):o.parentNode&&o.parentNode.removeChild(o),r?t.insertBefore(this.previousSibling,o):t.replaceChild(o,this)}}}));function ks(){return location.protocol==="file:"?_t(`${new URL("search/search_index.js",Or.base)}`).pipe(m(()=>__index),Z(1)):ze(new URL("search/search_index.json",Or.base))}document.documentElement.classList.remove("no-js");document.documentElement.classList.add("js");var ct=an(),Kt=bn(),Ht=yn(Kt),mo=hn(),ke=Ln(),Lr=Wt("(min-width: 60em)"),Vi=Wt("(min-width: 76.25em)"),Ni=xn(),Or=Te(),zi=document.forms.namedItem("search")?ks():tt,fo=new T;di({alert$:fo});ui({document$:ct});var uo=new T,qi=kt(Or.base);V("navigation.instant")&&gi({sitemap$:qi,location$:Kt,viewport$:ke,progress$:uo}).subscribe(ct);var Di;((Di=Or.version)==null?void 0:Di.provider)==="mike"&&Ti({document$:ct});L(Kt,Ht).pipe(nt(125)).subscribe(()=>{at("drawer",!1),at("search",!1)});mo.pipe(g(({mode:e})=>e==="global")).subscribe(e=>{switch(e.type){case"p":case",":let t=ue("link[rel=prev]");typeof t!="undefined"&&st(t);break;case"n":case".":let r=ue("link[rel=next]");typeof r!="undefined"&&st(r);break;case"Enter":let o=Ne();o instanceof HTMLLabelElement&&o.click()}});Fi({viewport$:ke,document$:ct});ji({document$:ct,tablet$:Lr});Ui({document$:ct});Wi({viewport$:ke,tablet$:Lr});var ft=ai(Ce("header"),{viewport$:ke}),qt=ct.pipe(m(()=>Ce("main")),b(e=>pi(e,{viewport$:ke,header$:ft})),Z(1)),Hs=L(...me("consent").map(e=>An(e,{target$:Ht})),...me("dialog").map(e=>ni(e,{alert$:fo})),...me("palette").map(e=>li(e)),...me("progress").map(e=>mi(e,{progress$:uo})),...me("search").map(e=>_i(e,{index$:zi,keyboard$:mo})),...me("source").map(e=>$i(e))),$s=H(()=>L(...me("announce").map(e=>_n(e)),...me("content").map(e=>oi(e,{sitemap$:qi,viewport$:ke,target$:Ht,print$:Ni})),...me("content").map(e=>V("search.highlight")?Ai(e,{index$:zi,location$:Kt}):y),...me("header").map(e=>si(e,{viewport$:ke,header$:ft,main$:qt})),...me("header-title").map(e=>ci(e,{viewport$:ke,header$:ft})),...me("sidebar").map(e=>e.getAttribute("data-md-type")==="navigation"?eo(Vi,()=>lo(e,{viewport$:ke,header$:ft,main$:qt})):eo(Lr,()=>lo(e,{viewport$:ke,header$:ft,main$:qt}))),...me("tabs").map(e=>Pi(e,{viewport$:ke,header$:ft})),...me("toc").map(e=>Ri(e,{viewport$:ke,header$:ft,main$:qt,target$:Ht})),...me("top").map(e=>Ii(e,{viewport$:ke,header$:ft,main$:qt,target$:Ht})))),Ki=ct.pipe(b(()=>$s),Ve(Hs),Z(1));Ki.subscribe();window.document$=ct;window.location$=Kt;window.target$=Ht;window.keyboard$=mo;window.viewport$=ke;window.tablet$=Lr;window.screen$=Vi;window.print$=Ni;window.alert$=fo;window.progress$=uo;window.component$=Ki;})(); +//# sourceMappingURL=bundle.79ae519e.min.js.map + diff --git a/zh/assets/javascripts/bundle.79ae519e.min.js.map b/zh/assets/javascripts/bundle.79ae519e.min.js.map new file mode 100644 index 0000000..5cf0289 --- /dev/null +++ b/zh/assets/javascripts/bundle.79ae519e.min.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["node_modules/focus-visible/dist/focus-visible.js", "node_modules/escape-html/index.js", "node_modules/clipboard/dist/clipboard.js", "src/templates/assets/javascripts/bundle.ts", "node_modules/tslib/tslib.es6.mjs", "node_modules/rxjs/src/internal/util/isFunction.ts", "node_modules/rxjs/src/internal/util/createErrorClass.ts", "node_modules/rxjs/src/internal/util/UnsubscriptionError.ts", "node_modules/rxjs/src/internal/util/arrRemove.ts", "node_modules/rxjs/src/internal/Subscription.ts", "node_modules/rxjs/src/internal/config.ts", "node_modules/rxjs/src/internal/scheduler/timeoutProvider.ts", "node_modules/rxjs/src/internal/util/reportUnhandledError.ts", "node_modules/rxjs/src/internal/util/noop.ts", "node_modules/rxjs/src/internal/NotificationFactories.ts", "node_modules/rxjs/src/internal/util/errorContext.ts", "node_modules/rxjs/src/internal/Subscriber.ts", "node_modules/rxjs/src/internal/symbol/observable.ts", "node_modules/rxjs/src/internal/util/identity.ts", "node_modules/rxjs/src/internal/util/pipe.ts", "node_modules/rxjs/src/internal/Observable.ts", "node_modules/rxjs/src/internal/util/lift.ts", "node_modules/rxjs/src/internal/operators/OperatorSubscriber.ts", "node_modules/rxjs/src/internal/scheduler/animationFrameProvider.ts", "node_modules/rxjs/src/internal/util/ObjectUnsubscribedError.ts", "node_modules/rxjs/src/internal/Subject.ts", "node_modules/rxjs/src/internal/BehaviorSubject.ts", "node_modules/rxjs/src/internal/scheduler/dateTimestampProvider.ts", "node_modules/rxjs/src/internal/ReplaySubject.ts", "node_modules/rxjs/src/internal/scheduler/Action.ts", "node_modules/rxjs/src/internal/scheduler/intervalProvider.ts", "node_modules/rxjs/src/internal/scheduler/AsyncAction.ts", "node_modules/rxjs/src/internal/Scheduler.ts", "node_modules/rxjs/src/internal/scheduler/AsyncScheduler.ts", "node_modules/rxjs/src/internal/scheduler/async.ts", "node_modules/rxjs/src/internal/scheduler/QueueAction.ts", "node_modules/rxjs/src/internal/scheduler/QueueScheduler.ts", "node_modules/rxjs/src/internal/scheduler/queue.ts", "node_modules/rxjs/src/internal/scheduler/AnimationFrameAction.ts", "node_modules/rxjs/src/internal/scheduler/AnimationFrameScheduler.ts", "node_modules/rxjs/src/internal/scheduler/animationFrame.ts", "node_modules/rxjs/src/internal/observable/empty.ts", "node_modules/rxjs/src/internal/util/isScheduler.ts", "node_modules/rxjs/src/internal/util/args.ts", "node_modules/rxjs/src/internal/util/isArrayLike.ts", "node_modules/rxjs/src/internal/util/isPromise.ts", "node_modules/rxjs/src/internal/util/isInteropObservable.ts", "node_modules/rxjs/src/internal/util/isAsyncIterable.ts", "node_modules/rxjs/src/internal/util/throwUnobservableError.ts", "node_modules/rxjs/src/internal/symbol/iterator.ts", "node_modules/rxjs/src/internal/util/isIterable.ts", "node_modules/rxjs/src/internal/util/isReadableStreamLike.ts", "node_modules/rxjs/src/internal/observable/innerFrom.ts", "node_modules/rxjs/src/internal/util/executeSchedule.ts", "node_modules/rxjs/src/internal/operators/observeOn.ts", "node_modules/rxjs/src/internal/operators/subscribeOn.ts", "node_modules/rxjs/src/internal/scheduled/scheduleObservable.ts", "node_modules/rxjs/src/internal/scheduled/schedulePromise.ts", "node_modules/rxjs/src/internal/scheduled/scheduleArray.ts", "node_modules/rxjs/src/internal/scheduled/scheduleIterable.ts", "node_modules/rxjs/src/internal/scheduled/scheduleAsyncIterable.ts", "node_modules/rxjs/src/internal/scheduled/scheduleReadableStreamLike.ts", "node_modules/rxjs/src/internal/scheduled/scheduled.ts", "node_modules/rxjs/src/internal/observable/from.ts", "node_modules/rxjs/src/internal/observable/of.ts", "node_modules/rxjs/src/internal/observable/throwError.ts", "node_modules/rxjs/src/internal/util/EmptyError.ts", "node_modules/rxjs/src/internal/util/isDate.ts", "node_modules/rxjs/src/internal/operators/map.ts", "node_modules/rxjs/src/internal/util/mapOneOrManyArgs.ts", "node_modules/rxjs/src/internal/util/argsArgArrayOrObject.ts", "node_modules/rxjs/src/internal/util/createObject.ts", "node_modules/rxjs/src/internal/observable/combineLatest.ts", "node_modules/rxjs/src/internal/operators/mergeInternals.ts", "node_modules/rxjs/src/internal/operators/mergeMap.ts", "node_modules/rxjs/src/internal/operators/mergeAll.ts", "node_modules/rxjs/src/internal/operators/concatAll.ts", "node_modules/rxjs/src/internal/observable/concat.ts", "node_modules/rxjs/src/internal/observable/defer.ts", "node_modules/rxjs/src/internal/observable/fromEvent.ts", "node_modules/rxjs/src/internal/observable/fromEventPattern.ts", "node_modules/rxjs/src/internal/observable/timer.ts", "node_modules/rxjs/src/internal/observable/merge.ts", "node_modules/rxjs/src/internal/observable/never.ts", "node_modules/rxjs/src/internal/util/argsOrArgArray.ts", "node_modules/rxjs/src/internal/operators/filter.ts", "node_modules/rxjs/src/internal/observable/zip.ts", "node_modules/rxjs/src/internal/operators/audit.ts", "node_modules/rxjs/src/internal/operators/auditTime.ts", "node_modules/rxjs/src/internal/operators/bufferCount.ts", "node_modules/rxjs/src/internal/operators/catchError.ts", "node_modules/rxjs/src/internal/operators/scanInternals.ts", "node_modules/rxjs/src/internal/operators/combineLatest.ts", "node_modules/rxjs/src/internal/operators/combineLatestWith.ts", "node_modules/rxjs/src/internal/operators/debounce.ts", "node_modules/rxjs/src/internal/operators/debounceTime.ts", "node_modules/rxjs/src/internal/operators/defaultIfEmpty.ts", "node_modules/rxjs/src/internal/operators/take.ts", "node_modules/rxjs/src/internal/operators/ignoreElements.ts", "node_modules/rxjs/src/internal/operators/mapTo.ts", "node_modules/rxjs/src/internal/operators/delayWhen.ts", "node_modules/rxjs/src/internal/operators/delay.ts", "node_modules/rxjs/src/internal/operators/distinct.ts", "node_modules/rxjs/src/internal/operators/distinctUntilChanged.ts", "node_modules/rxjs/src/internal/operators/distinctUntilKeyChanged.ts", "node_modules/rxjs/src/internal/operators/throwIfEmpty.ts", "node_modules/rxjs/src/internal/operators/endWith.ts", "node_modules/rxjs/src/internal/operators/exhaustMap.ts", "node_modules/rxjs/src/internal/operators/finalize.ts", "node_modules/rxjs/src/internal/operators/first.ts", "node_modules/rxjs/src/internal/operators/takeLast.ts", "node_modules/rxjs/src/internal/operators/merge.ts", "node_modules/rxjs/src/internal/operators/mergeWith.ts", "node_modules/rxjs/src/internal/operators/repeat.ts", "node_modules/rxjs/src/internal/operators/scan.ts", "node_modules/rxjs/src/internal/operators/share.ts", "node_modules/rxjs/src/internal/operators/shareReplay.ts", "node_modules/rxjs/src/internal/operators/skip.ts", "node_modules/rxjs/src/internal/operators/skipUntil.ts", "node_modules/rxjs/src/internal/operators/startWith.ts", "node_modules/rxjs/src/internal/operators/switchMap.ts", "node_modules/rxjs/src/internal/operators/takeUntil.ts", "node_modules/rxjs/src/internal/operators/takeWhile.ts", "node_modules/rxjs/src/internal/operators/tap.ts", "node_modules/rxjs/src/internal/operators/throttle.ts", "node_modules/rxjs/src/internal/operators/throttleTime.ts", "node_modules/rxjs/src/internal/operators/withLatestFrom.ts", "node_modules/rxjs/src/internal/operators/zip.ts", "node_modules/rxjs/src/internal/operators/zipWith.ts", "src/templates/assets/javascripts/browser/document/index.ts", "src/templates/assets/javascripts/browser/element/_/index.ts", "src/templates/assets/javascripts/browser/element/focus/index.ts", "src/templates/assets/javascripts/browser/element/hover/index.ts", "src/templates/assets/javascripts/utilities/h/index.ts", "src/templates/assets/javascripts/utilities/round/index.ts", "src/templates/assets/javascripts/browser/script/index.ts", "src/templates/assets/javascripts/browser/element/size/_/index.ts", "src/templates/assets/javascripts/browser/element/size/content/index.ts", "src/templates/assets/javascripts/browser/element/offset/_/index.ts", "src/templates/assets/javascripts/browser/element/offset/content/index.ts", "src/templates/assets/javascripts/browser/element/visibility/index.ts", "src/templates/assets/javascripts/browser/toggle/index.ts", "src/templates/assets/javascripts/browser/keyboard/index.ts", "src/templates/assets/javascripts/browser/location/_/index.ts", "src/templates/assets/javascripts/browser/location/hash/index.ts", "src/templates/assets/javascripts/browser/media/index.ts", "src/templates/assets/javascripts/browser/request/index.ts", "src/templates/assets/javascripts/browser/viewport/offset/index.ts", "src/templates/assets/javascripts/browser/viewport/size/index.ts", "src/templates/assets/javascripts/browser/viewport/_/index.ts", "src/templates/assets/javascripts/browser/viewport/at/index.ts", "src/templates/assets/javascripts/browser/worker/index.ts", "src/templates/assets/javascripts/_/index.ts", "src/templates/assets/javascripts/components/_/index.ts", "src/templates/assets/javascripts/components/announce/index.ts", "src/templates/assets/javascripts/components/consent/index.ts", "src/templates/assets/javascripts/templates/tooltip/index.tsx", "src/templates/assets/javascripts/templates/annotation/index.tsx", "src/templates/assets/javascripts/templates/clipboard/index.tsx", "src/templates/assets/javascripts/templates/search/index.tsx", "src/templates/assets/javascripts/templates/source/index.tsx", "src/templates/assets/javascripts/templates/tabbed/index.tsx", "src/templates/assets/javascripts/templates/table/index.tsx", "src/templates/assets/javascripts/templates/version/index.tsx", "src/templates/assets/javascripts/components/tooltip2/index.ts", "src/templates/assets/javascripts/components/content/annotation/_/index.ts", "src/templates/assets/javascripts/components/content/annotation/list/index.ts", "src/templates/assets/javascripts/components/content/annotation/block/index.ts", "src/templates/assets/javascripts/components/content/code/_/index.ts", "src/templates/assets/javascripts/components/content/details/index.ts", "src/templates/assets/javascripts/components/content/link/index.ts", "src/templates/assets/javascripts/components/content/mermaid/index.css", "src/templates/assets/javascripts/components/content/mermaid/index.ts", "src/templates/assets/javascripts/components/content/table/index.ts", "src/templates/assets/javascripts/components/content/tabs/index.ts", "src/templates/assets/javascripts/components/content/_/index.ts", "src/templates/assets/javascripts/components/dialog/index.ts", "src/templates/assets/javascripts/components/tooltip/index.ts", "src/templates/assets/javascripts/components/header/_/index.ts", "src/templates/assets/javascripts/components/header/title/index.ts", "src/templates/assets/javascripts/components/main/index.ts", "src/templates/assets/javascripts/components/palette/index.ts", "src/templates/assets/javascripts/components/progress/index.ts", "src/templates/assets/javascripts/integrations/sitemap/index.ts", "src/templates/assets/javascripts/integrations/alternate/index.ts", "src/templates/assets/javascripts/integrations/clipboard/index.ts", "src/templates/assets/javascripts/integrations/instant/index.ts", "src/templates/assets/javascripts/integrations/search/highlighter/index.ts", "src/templates/assets/javascripts/integrations/search/worker/message/index.ts", "src/templates/assets/javascripts/integrations/search/worker/_/index.ts", "src/templates/assets/javascripts/integrations/version/findurl/index.ts", "src/templates/assets/javascripts/integrations/version/index.ts", "src/templates/assets/javascripts/components/search/query/index.ts", "src/templates/assets/javascripts/components/search/result/index.ts", "src/templates/assets/javascripts/components/search/share/index.ts", "src/templates/assets/javascripts/components/search/suggest/index.ts", "src/templates/assets/javascripts/components/search/_/index.ts", "src/templates/assets/javascripts/components/search/highlight/index.ts", "src/templates/assets/javascripts/components/sidebar/index.ts", "src/templates/assets/javascripts/components/source/facts/github/index.ts", "src/templates/assets/javascripts/components/source/facts/gitlab/index.ts", "src/templates/assets/javascripts/components/source/facts/_/index.ts", "src/templates/assets/javascripts/components/source/_/index.ts", "src/templates/assets/javascripts/components/tabs/index.ts", "src/templates/assets/javascripts/components/toc/index.ts", "src/templates/assets/javascripts/components/top/index.ts", "src/templates/assets/javascripts/patches/ellipsis/index.ts", "src/templates/assets/javascripts/patches/indeterminate/index.ts", "src/templates/assets/javascripts/patches/scrollfix/index.ts", "src/templates/assets/javascripts/patches/scrolllock/index.ts", "src/templates/assets/javascripts/polyfills/index.ts"], + "sourcesContent": ["(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? factory() :\n typeof define === 'function' && define.amd ? define(factory) :\n (factory());\n}(this, (function () { 'use strict';\n\n /**\n * Applies the :focus-visible polyfill at the given scope.\n * A scope in this case is either the top-level Document or a Shadow Root.\n *\n * @param {(Document|ShadowRoot)} scope\n * @see https://github.com/WICG/focus-visible\n */\n function applyFocusVisiblePolyfill(scope) {\n var hadKeyboardEvent = true;\n var hadFocusVisibleRecently = false;\n var hadFocusVisibleRecentlyTimeout = null;\n\n var inputTypesAllowlist = {\n text: true,\n search: true,\n url: true,\n tel: true,\n email: true,\n password: true,\n number: true,\n date: true,\n month: true,\n week: true,\n time: true,\n datetime: true,\n 'datetime-local': true\n };\n\n /**\n * Helper function for legacy browsers and iframes which sometimes focus\n * elements like document, body, and non-interactive SVG.\n * @param {Element} el\n */\n function isValidFocusTarget(el) {\n if (\n el &&\n el !== document &&\n el.nodeName !== 'HTML' &&\n el.nodeName !== 'BODY' &&\n 'classList' in el &&\n 'contains' in el.classList\n ) {\n return true;\n }\n return false;\n }\n\n /**\n * Computes whether the given element should automatically trigger the\n * `focus-visible` class being added, i.e. whether it should always match\n * `:focus-visible` when focused.\n * @param {Element} el\n * @return {boolean}\n */\n function focusTriggersKeyboardModality(el) {\n var type = el.type;\n var tagName = el.tagName;\n\n if (tagName === 'INPUT' && inputTypesAllowlist[type] && !el.readOnly) {\n return true;\n }\n\n if (tagName === 'TEXTAREA' && !el.readOnly) {\n return true;\n }\n\n if (el.isContentEditable) {\n return true;\n }\n\n return false;\n }\n\n /**\n * Add the `focus-visible` class to the given element if it was not added by\n * the author.\n * @param {Element} el\n */\n function addFocusVisibleClass(el) {\n if (el.classList.contains('focus-visible')) {\n return;\n }\n el.classList.add('focus-visible');\n el.setAttribute('data-focus-visible-added', '');\n }\n\n /**\n * Remove the `focus-visible` class from the given element if it was not\n * originally added by the author.\n * @param {Element} el\n */\n function removeFocusVisibleClass(el) {\n if (!el.hasAttribute('data-focus-visible-added')) {\n return;\n }\n el.classList.remove('focus-visible');\n el.removeAttribute('data-focus-visible-added');\n }\n\n /**\n * If the most recent user interaction was via the keyboard;\n * and the key press did not include a meta, alt/option, or control key;\n * then the modality is keyboard. Otherwise, the modality is not keyboard.\n * Apply `focus-visible` to any current active element and keep track\n * of our keyboard modality state with `hadKeyboardEvent`.\n * @param {KeyboardEvent} e\n */\n function onKeyDown(e) {\n if (e.metaKey || e.altKey || e.ctrlKey) {\n return;\n }\n\n if (isValidFocusTarget(scope.activeElement)) {\n addFocusVisibleClass(scope.activeElement);\n }\n\n hadKeyboardEvent = true;\n }\n\n /**\n * If at any point a user clicks with a pointing device, ensure that we change\n * the modality away from keyboard.\n * This avoids the situation where a user presses a key on an already focused\n * element, and then clicks on a different element, focusing it with a\n * pointing device, while we still think we're in keyboard modality.\n * @param {Event} e\n */\n function onPointerDown(e) {\n hadKeyboardEvent = false;\n }\n\n /**\n * On `focus`, add the `focus-visible` class to the target if:\n * - the target received focus as a result of keyboard navigation, or\n * - the event target is an element that will likely require interaction\n * via the keyboard (e.g. a text box)\n * @param {Event} e\n */\n function onFocus(e) {\n // Prevent IE from focusing the document or HTML element.\n if (!isValidFocusTarget(e.target)) {\n return;\n }\n\n if (hadKeyboardEvent || focusTriggersKeyboardModality(e.target)) {\n addFocusVisibleClass(e.target);\n }\n }\n\n /**\n * On `blur`, remove the `focus-visible` class from the target.\n * @param {Event} e\n */\n function onBlur(e) {\n if (!isValidFocusTarget(e.target)) {\n return;\n }\n\n if (\n e.target.classList.contains('focus-visible') ||\n e.target.hasAttribute('data-focus-visible-added')\n ) {\n // To detect a tab/window switch, we look for a blur event followed\n // rapidly by a visibility change.\n // If we don't see a visibility change within 100ms, it's probably a\n // regular focus change.\n hadFocusVisibleRecently = true;\n window.clearTimeout(hadFocusVisibleRecentlyTimeout);\n hadFocusVisibleRecentlyTimeout = window.setTimeout(function() {\n hadFocusVisibleRecently = false;\n }, 100);\n removeFocusVisibleClass(e.target);\n }\n }\n\n /**\n * If the user changes tabs, keep track of whether or not the previously\n * focused element had .focus-visible.\n * @param {Event} e\n */\n function onVisibilityChange(e) {\n if (document.visibilityState === 'hidden') {\n // If the tab becomes active again, the browser will handle calling focus\n // on the element (Safari actually calls it twice).\n // If this tab change caused a blur on an element with focus-visible,\n // re-apply the class when the user switches back to the tab.\n if (hadFocusVisibleRecently) {\n hadKeyboardEvent = true;\n }\n addInitialPointerMoveListeners();\n }\n }\n\n /**\n * Add a group of listeners to detect usage of any pointing devices.\n * These listeners will be added when the polyfill first loads, and anytime\n * the window is blurred, so that they are active when the window regains\n * focus.\n */\n function addInitialPointerMoveListeners() {\n document.addEventListener('mousemove', onInitialPointerMove);\n document.addEventListener('mousedown', onInitialPointerMove);\n document.addEventListener('mouseup', onInitialPointerMove);\n document.addEventListener('pointermove', onInitialPointerMove);\n document.addEventListener('pointerdown', onInitialPointerMove);\n document.addEventListener('pointerup', onInitialPointerMove);\n document.addEventListener('touchmove', onInitialPointerMove);\n document.addEventListener('touchstart', onInitialPointerMove);\n document.addEventListener('touchend', onInitialPointerMove);\n }\n\n function removeInitialPointerMoveListeners() {\n document.removeEventListener('mousemove', onInitialPointerMove);\n document.removeEventListener('mousedown', onInitialPointerMove);\n document.removeEventListener('mouseup', onInitialPointerMove);\n document.removeEventListener('pointermove', onInitialPointerMove);\n document.removeEventListener('pointerdown', onInitialPointerMove);\n document.removeEventListener('pointerup', onInitialPointerMove);\n document.removeEventListener('touchmove', onInitialPointerMove);\n document.removeEventListener('touchstart', onInitialPointerMove);\n document.removeEventListener('touchend', onInitialPointerMove);\n }\n\n /**\n * When the polfyill first loads, assume the user is in keyboard modality.\n * If any event is received from a pointing device (e.g. mouse, pointer,\n * touch), turn off keyboard modality.\n * This accounts for situations where focus enters the page from the URL bar.\n * @param {Event} e\n */\n function onInitialPointerMove(e) {\n // Work around a Safari quirk that fires a mousemove on whenever the\n // window blurs, even if you're tabbing out of the page. \u00AF\\_(\u30C4)_/\u00AF\n if (e.target.nodeName && e.target.nodeName.toLowerCase() === 'html') {\n return;\n }\n\n hadKeyboardEvent = false;\n removeInitialPointerMoveListeners();\n }\n\n // For some kinds of state, we are interested in changes at the global scope\n // only. For example, global pointer input, global key presses and global\n // visibility change should affect the state at every scope:\n document.addEventListener('keydown', onKeyDown, true);\n document.addEventListener('mousedown', onPointerDown, true);\n document.addEventListener('pointerdown', onPointerDown, true);\n document.addEventListener('touchstart', onPointerDown, true);\n document.addEventListener('visibilitychange', onVisibilityChange, true);\n\n addInitialPointerMoveListeners();\n\n // For focus and blur, we specifically care about state changes in the local\n // scope. This is because focus / blur events that originate from within a\n // shadow root are not re-dispatched from the host element if it was already\n // the active element in its own scope:\n scope.addEventListener('focus', onFocus, true);\n scope.addEventListener('blur', onBlur, true);\n\n // We detect that a node is a ShadowRoot by ensuring that it is a\n // DocumentFragment and also has a host property. This check covers native\n // implementation and polyfill implementation transparently. If we only cared\n // about the native implementation, we could just check if the scope was\n // an instance of a ShadowRoot.\n if (scope.nodeType === Node.DOCUMENT_FRAGMENT_NODE && scope.host) {\n // Since a ShadowRoot is a special kind of DocumentFragment, it does not\n // have a root element to add a class to. So, we add this attribute to the\n // host element instead:\n scope.host.setAttribute('data-js-focus-visible', '');\n } else if (scope.nodeType === Node.DOCUMENT_NODE) {\n document.documentElement.classList.add('js-focus-visible');\n document.documentElement.setAttribute('data-js-focus-visible', '');\n }\n }\n\n // It is important to wrap all references to global window and document in\n // these checks to support server-side rendering use cases\n // @see https://github.com/WICG/focus-visible/issues/199\n if (typeof window !== 'undefined' && typeof document !== 'undefined') {\n // Make the polyfill helper globally available. This can be used as a signal\n // to interested libraries that wish to coordinate with the polyfill for e.g.,\n // applying the polyfill to a shadow root:\n window.applyFocusVisiblePolyfill = applyFocusVisiblePolyfill;\n\n // Notify interested libraries of the polyfill's presence, in case the\n // polyfill was loaded lazily:\n var event;\n\n try {\n event = new CustomEvent('focus-visible-polyfill-ready');\n } catch (error) {\n // IE11 does not support using CustomEvent as a constructor directly:\n event = document.createEvent('CustomEvent');\n event.initCustomEvent('focus-visible-polyfill-ready', false, false, {});\n }\n\n window.dispatchEvent(event);\n }\n\n if (typeof document !== 'undefined') {\n // Apply the polyfill to the global document, so that no JavaScript\n // coordination is required to use the polyfill in the top-level document:\n applyFocusVisiblePolyfill(document);\n }\n\n})));\n", "/*!\n * escape-html\n * Copyright(c) 2012-2013 TJ Holowaychuk\n * Copyright(c) 2015 Andreas Lubbe\n * Copyright(c) 2015 Tiancheng \"Timothy\" Gu\n * MIT Licensed\n */\n\n'use strict';\n\n/**\n * Module variables.\n * @private\n */\n\nvar matchHtmlRegExp = /[\"'&<>]/;\n\n/**\n * Module exports.\n * @public\n */\n\nmodule.exports = escapeHtml;\n\n/**\n * Escape special characters in the given string of html.\n *\n * @param {string} string The string to escape for inserting into HTML\n * @return {string}\n * @public\n */\n\nfunction escapeHtml(string) {\n var str = '' + string;\n var match = matchHtmlRegExp.exec(str);\n\n if (!match) {\n return str;\n }\n\n var escape;\n var html = '';\n var index = 0;\n var lastIndex = 0;\n\n for (index = match.index; index < str.length; index++) {\n switch (str.charCodeAt(index)) {\n case 34: // \"\n escape = '"';\n break;\n case 38: // &\n escape = '&';\n break;\n case 39: // '\n escape = ''';\n break;\n case 60: // <\n escape = '<';\n break;\n case 62: // >\n escape = '>';\n break;\n default:\n continue;\n }\n\n if (lastIndex !== index) {\n html += str.substring(lastIndex, index);\n }\n\n lastIndex = index + 1;\n html += escape;\n }\n\n return lastIndex !== index\n ? html + str.substring(lastIndex, index)\n : html;\n}\n", "/*!\n * clipboard.js v2.0.11\n * https://clipboardjs.com/\n *\n * Licensed MIT \u00A9 Zeno Rocha\n */\n(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"ClipboardJS\"] = factory();\n\telse\n\t\troot[\"ClipboardJS\"] = factory();\n})(this, function() {\nreturn /******/ (function() { // webpackBootstrap\n/******/ \tvar __webpack_modules__ = ({\n\n/***/ 686:\n/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n\n// EXPORTS\n__webpack_require__.d(__webpack_exports__, {\n \"default\": function() { return /* binding */ clipboard; }\n});\n\n// EXTERNAL MODULE: ./node_modules/tiny-emitter/index.js\nvar tiny_emitter = __webpack_require__(279);\nvar tiny_emitter_default = /*#__PURE__*/__webpack_require__.n(tiny_emitter);\n// EXTERNAL MODULE: ./node_modules/good-listener/src/listen.js\nvar listen = __webpack_require__(370);\nvar listen_default = /*#__PURE__*/__webpack_require__.n(listen);\n// EXTERNAL MODULE: ./node_modules/select/src/select.js\nvar src_select = __webpack_require__(817);\nvar select_default = /*#__PURE__*/__webpack_require__.n(src_select);\n;// CONCATENATED MODULE: ./src/common/command.js\n/**\n * Executes a given operation type.\n * @param {String} type\n * @return {Boolean}\n */\nfunction command(type) {\n try {\n return document.execCommand(type);\n } catch (err) {\n return false;\n }\n}\n;// CONCATENATED MODULE: ./src/actions/cut.js\n\n\n/**\n * Cut action wrapper.\n * @param {String|HTMLElement} target\n * @return {String}\n */\n\nvar ClipboardActionCut = function ClipboardActionCut(target) {\n var selectedText = select_default()(target);\n command('cut');\n return selectedText;\n};\n\n/* harmony default export */ var actions_cut = (ClipboardActionCut);\n;// CONCATENATED MODULE: ./src/common/create-fake-element.js\n/**\n * Creates a fake textarea element with a value.\n * @param {String} value\n * @return {HTMLElement}\n */\nfunction createFakeElement(value) {\n var isRTL = document.documentElement.getAttribute('dir') === 'rtl';\n var fakeElement = document.createElement('textarea'); // Prevent zooming on iOS\n\n fakeElement.style.fontSize = '12pt'; // Reset box model\n\n fakeElement.style.border = '0';\n fakeElement.style.padding = '0';\n fakeElement.style.margin = '0'; // Move element out of screen horizontally\n\n fakeElement.style.position = 'absolute';\n fakeElement.style[isRTL ? 'right' : 'left'] = '-9999px'; // Move element to the same position vertically\n\n var yPosition = window.pageYOffset || document.documentElement.scrollTop;\n fakeElement.style.top = \"\".concat(yPosition, \"px\");\n fakeElement.setAttribute('readonly', '');\n fakeElement.value = value;\n return fakeElement;\n}\n;// CONCATENATED MODULE: ./src/actions/copy.js\n\n\n\n/**\n * Create fake copy action wrapper using a fake element.\n * @param {String} target\n * @param {Object} options\n * @return {String}\n */\n\nvar fakeCopyAction = function fakeCopyAction(value, options) {\n var fakeElement = createFakeElement(value);\n options.container.appendChild(fakeElement);\n var selectedText = select_default()(fakeElement);\n command('copy');\n fakeElement.remove();\n return selectedText;\n};\n/**\n * Copy action wrapper.\n * @param {String|HTMLElement} target\n * @param {Object} options\n * @return {String}\n */\n\n\nvar ClipboardActionCopy = function ClipboardActionCopy(target) {\n var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {\n container: document.body\n };\n var selectedText = '';\n\n if (typeof target === 'string') {\n selectedText = fakeCopyAction(target, options);\n } else if (target instanceof HTMLInputElement && !['text', 'search', 'url', 'tel', 'password'].includes(target === null || target === void 0 ? void 0 : target.type)) {\n // If input type doesn't support `setSelectionRange`. Simulate it. https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/setSelectionRange\n selectedText = fakeCopyAction(target.value, options);\n } else {\n selectedText = select_default()(target);\n command('copy');\n }\n\n return selectedText;\n};\n\n/* harmony default export */ var actions_copy = (ClipboardActionCopy);\n;// CONCATENATED MODULE: ./src/actions/default.js\nfunction _typeof(obj) { \"@babel/helpers - typeof\"; if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return _typeof(obj); }\n\n\n\n/**\n * Inner function which performs selection from either `text` or `target`\n * properties and then executes copy or cut operations.\n * @param {Object} options\n */\n\nvar ClipboardActionDefault = function ClipboardActionDefault() {\n var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n // Defines base properties passed from constructor.\n var _options$action = options.action,\n action = _options$action === void 0 ? 'copy' : _options$action,\n container = options.container,\n target = options.target,\n text = options.text; // Sets the `action` to be performed which can be either 'copy' or 'cut'.\n\n if (action !== 'copy' && action !== 'cut') {\n throw new Error('Invalid \"action\" value, use either \"copy\" or \"cut\"');\n } // Sets the `target` property using an element that will be have its content copied.\n\n\n if (target !== undefined) {\n if (target && _typeof(target) === 'object' && target.nodeType === 1) {\n if (action === 'copy' && target.hasAttribute('disabled')) {\n throw new Error('Invalid \"target\" attribute. Please use \"readonly\" instead of \"disabled\" attribute');\n }\n\n if (action === 'cut' && (target.hasAttribute('readonly') || target.hasAttribute('disabled'))) {\n throw new Error('Invalid \"target\" attribute. You can\\'t cut text from elements with \"readonly\" or \"disabled\" attributes');\n }\n } else {\n throw new Error('Invalid \"target\" value, use a valid Element');\n }\n } // Define selection strategy based on `text` property.\n\n\n if (text) {\n return actions_copy(text, {\n container: container\n });\n } // Defines which selection strategy based on `target` property.\n\n\n if (target) {\n return action === 'cut' ? actions_cut(target) : actions_copy(target, {\n container: container\n });\n }\n};\n\n/* harmony default export */ var actions_default = (ClipboardActionDefault);\n;// CONCATENATED MODULE: ./src/clipboard.js\nfunction clipboard_typeof(obj) { \"@babel/helpers - typeof\"; if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { clipboard_typeof = function _typeof(obj) { return typeof obj; }; } else { clipboard_typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return clipboard_typeof(obj); }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function\"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }\n\nfunction _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }\n\nfunction _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }\n\nfunction _possibleConstructorReturn(self, call) { if (call && (clipboard_typeof(call) === \"object\" || typeof call === \"function\")) { return call; } return _assertThisInitialized(self); }\n\nfunction _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return self; }\n\nfunction _isNativeReflectConstruct() { if (typeof Reflect === \"undefined\" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === \"function\") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }\n\nfunction _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }\n\n\n\n\n\n\n/**\n * Helper function to retrieve attribute value.\n * @param {String} suffix\n * @param {Element} element\n */\n\nfunction getAttributeValue(suffix, element) {\n var attribute = \"data-clipboard-\".concat(suffix);\n\n if (!element.hasAttribute(attribute)) {\n return;\n }\n\n return element.getAttribute(attribute);\n}\n/**\n * Base class which takes one or more elements, adds event listeners to them,\n * and instantiates a new `ClipboardAction` on each click.\n */\n\n\nvar Clipboard = /*#__PURE__*/function (_Emitter) {\n _inherits(Clipboard, _Emitter);\n\n var _super = _createSuper(Clipboard);\n\n /**\n * @param {String|HTMLElement|HTMLCollection|NodeList} trigger\n * @param {Object} options\n */\n function Clipboard(trigger, options) {\n var _this;\n\n _classCallCheck(this, Clipboard);\n\n _this = _super.call(this);\n\n _this.resolveOptions(options);\n\n _this.listenClick(trigger);\n\n return _this;\n }\n /**\n * Defines if attributes would be resolved using internal setter functions\n * or custom functions that were passed in the constructor.\n * @param {Object} options\n */\n\n\n _createClass(Clipboard, [{\n key: \"resolveOptions\",\n value: function resolveOptions() {\n var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n this.action = typeof options.action === 'function' ? options.action : this.defaultAction;\n this.target = typeof options.target === 'function' ? options.target : this.defaultTarget;\n this.text = typeof options.text === 'function' ? options.text : this.defaultText;\n this.container = clipboard_typeof(options.container) === 'object' ? options.container : document.body;\n }\n /**\n * Adds a click event listener to the passed trigger.\n * @param {String|HTMLElement|HTMLCollection|NodeList} trigger\n */\n\n }, {\n key: \"listenClick\",\n value: function listenClick(trigger) {\n var _this2 = this;\n\n this.listener = listen_default()(trigger, 'click', function (e) {\n return _this2.onClick(e);\n });\n }\n /**\n * Defines a new `ClipboardAction` on each click event.\n * @param {Event} e\n */\n\n }, {\n key: \"onClick\",\n value: function onClick(e) {\n var trigger = e.delegateTarget || e.currentTarget;\n var action = this.action(trigger) || 'copy';\n var text = actions_default({\n action: action,\n container: this.container,\n target: this.target(trigger),\n text: this.text(trigger)\n }); // Fires an event based on the copy operation result.\n\n this.emit(text ? 'success' : 'error', {\n action: action,\n text: text,\n trigger: trigger,\n clearSelection: function clearSelection() {\n if (trigger) {\n trigger.focus();\n }\n\n window.getSelection().removeAllRanges();\n }\n });\n }\n /**\n * Default `action` lookup function.\n * @param {Element} trigger\n */\n\n }, {\n key: \"defaultAction\",\n value: function defaultAction(trigger) {\n return getAttributeValue('action', trigger);\n }\n /**\n * Default `target` lookup function.\n * @param {Element} trigger\n */\n\n }, {\n key: \"defaultTarget\",\n value: function defaultTarget(trigger) {\n var selector = getAttributeValue('target', trigger);\n\n if (selector) {\n return document.querySelector(selector);\n }\n }\n /**\n * Allow fire programmatically a copy action\n * @param {String|HTMLElement} target\n * @param {Object} options\n * @returns Text copied.\n */\n\n }, {\n key: \"defaultText\",\n\n /**\n * Default `text` lookup function.\n * @param {Element} trigger\n */\n value: function defaultText(trigger) {\n return getAttributeValue('text', trigger);\n }\n /**\n * Destroy lifecycle.\n */\n\n }, {\n key: \"destroy\",\n value: function destroy() {\n this.listener.destroy();\n }\n }], [{\n key: \"copy\",\n value: function copy(target) {\n var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {\n container: document.body\n };\n return actions_copy(target, options);\n }\n /**\n * Allow fire programmatically a cut action\n * @param {String|HTMLElement} target\n * @returns Text cutted.\n */\n\n }, {\n key: \"cut\",\n value: function cut(target) {\n return actions_cut(target);\n }\n /**\n * Returns the support of the given action, or all actions if no action is\n * given.\n * @param {String} [action]\n */\n\n }, {\n key: \"isSupported\",\n value: function isSupported() {\n var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ['copy', 'cut'];\n var actions = typeof action === 'string' ? [action] : action;\n var support = !!document.queryCommandSupported;\n actions.forEach(function (action) {\n support = support && !!document.queryCommandSupported(action);\n });\n return support;\n }\n }]);\n\n return Clipboard;\n}((tiny_emitter_default()));\n\n/* harmony default export */ var clipboard = (Clipboard);\n\n/***/ }),\n\n/***/ 828:\n/***/ (function(module) {\n\nvar DOCUMENT_NODE_TYPE = 9;\n\n/**\n * A polyfill for Element.matches()\n */\nif (typeof Element !== 'undefined' && !Element.prototype.matches) {\n var proto = Element.prototype;\n\n proto.matches = proto.matchesSelector ||\n proto.mozMatchesSelector ||\n proto.msMatchesSelector ||\n proto.oMatchesSelector ||\n proto.webkitMatchesSelector;\n}\n\n/**\n * Finds the closest parent that matches a selector.\n *\n * @param {Element} element\n * @param {String} selector\n * @return {Function}\n */\nfunction closest (element, selector) {\n while (element && element.nodeType !== DOCUMENT_NODE_TYPE) {\n if (typeof element.matches === 'function' &&\n element.matches(selector)) {\n return element;\n }\n element = element.parentNode;\n }\n}\n\nmodule.exports = closest;\n\n\n/***/ }),\n\n/***/ 438:\n/***/ (function(module, __unused_webpack_exports, __webpack_require__) {\n\nvar closest = __webpack_require__(828);\n\n/**\n * Delegates event to a selector.\n *\n * @param {Element} element\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @param {Boolean} useCapture\n * @return {Object}\n */\nfunction _delegate(element, selector, type, callback, useCapture) {\n var listenerFn = listener.apply(this, arguments);\n\n element.addEventListener(type, listenerFn, useCapture);\n\n return {\n destroy: function() {\n element.removeEventListener(type, listenerFn, useCapture);\n }\n }\n}\n\n/**\n * Delegates event to a selector.\n *\n * @param {Element|String|Array} [elements]\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @param {Boolean} useCapture\n * @return {Object}\n */\nfunction delegate(elements, selector, type, callback, useCapture) {\n // Handle the regular Element usage\n if (typeof elements.addEventListener === 'function') {\n return _delegate.apply(null, arguments);\n }\n\n // Handle Element-less usage, it defaults to global delegation\n if (typeof type === 'function') {\n // Use `document` as the first parameter, then apply arguments\n // This is a short way to .unshift `arguments` without running into deoptimizations\n return _delegate.bind(null, document).apply(null, arguments);\n }\n\n // Handle Selector-based usage\n if (typeof elements === 'string') {\n elements = document.querySelectorAll(elements);\n }\n\n // Handle Array-like based usage\n return Array.prototype.map.call(elements, function (element) {\n return _delegate(element, selector, type, callback, useCapture);\n });\n}\n\n/**\n * Finds closest match and invokes callback.\n *\n * @param {Element} element\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @return {Function}\n */\nfunction listener(element, selector, type, callback) {\n return function(e) {\n e.delegateTarget = closest(e.target, selector);\n\n if (e.delegateTarget) {\n callback.call(element, e);\n }\n }\n}\n\nmodule.exports = delegate;\n\n\n/***/ }),\n\n/***/ 879:\n/***/ (function(__unused_webpack_module, exports) {\n\n/**\n * Check if argument is a HTML element.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.node = function(value) {\n return value !== undefined\n && value instanceof HTMLElement\n && value.nodeType === 1;\n};\n\n/**\n * Check if argument is a list of HTML elements.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.nodeList = function(value) {\n var type = Object.prototype.toString.call(value);\n\n return value !== undefined\n && (type === '[object NodeList]' || type === '[object HTMLCollection]')\n && ('length' in value)\n && (value.length === 0 || exports.node(value[0]));\n};\n\n/**\n * Check if argument is a string.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.string = function(value) {\n return typeof value === 'string'\n || value instanceof String;\n};\n\n/**\n * Check if argument is a function.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.fn = function(value) {\n var type = Object.prototype.toString.call(value);\n\n return type === '[object Function]';\n};\n\n\n/***/ }),\n\n/***/ 370:\n/***/ (function(module, __unused_webpack_exports, __webpack_require__) {\n\nvar is = __webpack_require__(879);\nvar delegate = __webpack_require__(438);\n\n/**\n * Validates all params and calls the right\n * listener function based on its target type.\n *\n * @param {String|HTMLElement|HTMLCollection|NodeList} target\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listen(target, type, callback) {\n if (!target && !type && !callback) {\n throw new Error('Missing required arguments');\n }\n\n if (!is.string(type)) {\n throw new TypeError('Second argument must be a String');\n }\n\n if (!is.fn(callback)) {\n throw new TypeError('Third argument must be a Function');\n }\n\n if (is.node(target)) {\n return listenNode(target, type, callback);\n }\n else if (is.nodeList(target)) {\n return listenNodeList(target, type, callback);\n }\n else if (is.string(target)) {\n return listenSelector(target, type, callback);\n }\n else {\n throw new TypeError('First argument must be a String, HTMLElement, HTMLCollection, or NodeList');\n }\n}\n\n/**\n * Adds an event listener to a HTML element\n * and returns a remove listener function.\n *\n * @param {HTMLElement} node\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenNode(node, type, callback) {\n node.addEventListener(type, callback);\n\n return {\n destroy: function() {\n node.removeEventListener(type, callback);\n }\n }\n}\n\n/**\n * Add an event listener to a list of HTML elements\n * and returns a remove listener function.\n *\n * @param {NodeList|HTMLCollection} nodeList\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenNodeList(nodeList, type, callback) {\n Array.prototype.forEach.call(nodeList, function(node) {\n node.addEventListener(type, callback);\n });\n\n return {\n destroy: function() {\n Array.prototype.forEach.call(nodeList, function(node) {\n node.removeEventListener(type, callback);\n });\n }\n }\n}\n\n/**\n * Add an event listener to a selector\n * and returns a remove listener function.\n *\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenSelector(selector, type, callback) {\n return delegate(document.body, selector, type, callback);\n}\n\nmodule.exports = listen;\n\n\n/***/ }),\n\n/***/ 817:\n/***/ (function(module) {\n\nfunction select(element) {\n var selectedText;\n\n if (element.nodeName === 'SELECT') {\n element.focus();\n\n selectedText = element.value;\n }\n else if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') {\n var isReadOnly = element.hasAttribute('readonly');\n\n if (!isReadOnly) {\n element.setAttribute('readonly', '');\n }\n\n element.select();\n element.setSelectionRange(0, element.value.length);\n\n if (!isReadOnly) {\n element.removeAttribute('readonly');\n }\n\n selectedText = element.value;\n }\n else {\n if (element.hasAttribute('contenteditable')) {\n element.focus();\n }\n\n var selection = window.getSelection();\n var range = document.createRange();\n\n range.selectNodeContents(element);\n selection.removeAllRanges();\n selection.addRange(range);\n\n selectedText = selection.toString();\n }\n\n return selectedText;\n}\n\nmodule.exports = select;\n\n\n/***/ }),\n\n/***/ 279:\n/***/ (function(module) {\n\nfunction E () {\n // Keep this empty so it's easier to inherit from\n // (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3)\n}\n\nE.prototype = {\n on: function (name, callback, ctx) {\n var e = this.e || (this.e = {});\n\n (e[name] || (e[name] = [])).push({\n fn: callback,\n ctx: ctx\n });\n\n return this;\n },\n\n once: function (name, callback, ctx) {\n var self = this;\n function listener () {\n self.off(name, listener);\n callback.apply(ctx, arguments);\n };\n\n listener._ = callback\n return this.on(name, listener, ctx);\n },\n\n emit: function (name) {\n var data = [].slice.call(arguments, 1);\n var evtArr = ((this.e || (this.e = {}))[name] || []).slice();\n var i = 0;\n var len = evtArr.length;\n\n for (i; i < len; i++) {\n evtArr[i].fn.apply(evtArr[i].ctx, data);\n }\n\n return this;\n },\n\n off: function (name, callback) {\n var e = this.e || (this.e = {});\n var evts = e[name];\n var liveEvents = [];\n\n if (evts && callback) {\n for (var i = 0, len = evts.length; i < len; i++) {\n if (evts[i].fn !== callback && evts[i].fn._ !== callback)\n liveEvents.push(evts[i]);\n }\n }\n\n // Remove event from queue to prevent memory leak\n // Suggested by https://github.com/lazd\n // Ref: https://github.com/scottcorgan/tiny-emitter/commit/c6ebfaa9bc973b33d110a84a307742b7cf94c953#commitcomment-5024910\n\n (liveEvents.length)\n ? e[name] = liveEvents\n : delete e[name];\n\n return this;\n }\n};\n\nmodule.exports = E;\nmodule.exports.TinyEmitter = E;\n\n\n/***/ })\n\n/******/ \t});\n/************************************************************************/\n/******/ \t// The module cache\n/******/ \tvar __webpack_module_cache__ = {};\n/******/ \t\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(__webpack_module_cache__[moduleId]) {\n/******/ \t\t\treturn __webpack_module_cache__[moduleId].exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = __webpack_module_cache__[moduleId] = {\n/******/ \t\t\t// no module.id needed\n/******/ \t\t\t// no module.loaded needed\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/ \t\n/******/ \t\t// Execute the module function\n/******/ \t\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n/******/ \t\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/ \t\n/************************************************************************/\n/******/ \t/* webpack/runtime/compat get default export */\n/******/ \t!function() {\n/******/ \t\t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t\t__webpack_require__.n = function(module) {\n/******/ \t\t\tvar getter = module && module.__esModule ?\n/******/ \t\t\t\tfunction() { return module['default']; } :\n/******/ \t\t\t\tfunction() { return module; };\n/******/ \t\t\t__webpack_require__.d(getter, { a: getter });\n/******/ \t\t\treturn getter;\n/******/ \t\t};\n/******/ \t}();\n/******/ \t\n/******/ \t/* webpack/runtime/define property getters */\n/******/ \t!function() {\n/******/ \t\t// define getter functions for harmony exports\n/******/ \t\t__webpack_require__.d = function(exports, definition) {\n/******/ \t\t\tfor(var key in definition) {\n/******/ \t\t\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n/******/ \t\t\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n/******/ \t\t\t\t}\n/******/ \t\t\t}\n/******/ \t\t};\n/******/ \t}();\n/******/ \t\n/******/ \t/* webpack/runtime/hasOwnProperty shorthand */\n/******/ \t!function() {\n/******/ \t\t__webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }\n/******/ \t}();\n/******/ \t\n/************************************************************************/\n/******/ \t// module exports must be returned from runtime so entry inlining is disabled\n/******/ \t// startup\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(686);\n/******/ })()\n.default;\n});", "/*\n * Copyright (c) 2016-2025 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport \"focus-visible\"\n\nimport {\n EMPTY,\n NEVER,\n Observable,\n Subject,\n defer,\n delay,\n filter,\n map,\n merge,\n mergeWith,\n shareReplay,\n switchMap\n} from \"rxjs\"\n\nimport { configuration, feature } from \"./_\"\nimport {\n at,\n getActiveElement,\n getOptionalElement,\n requestJSON,\n setLocation,\n setToggle,\n watchDocument,\n watchKeyboard,\n watchLocation,\n watchLocationTarget,\n watchMedia,\n watchPrint,\n watchScript,\n watchViewport\n} from \"./browser\"\nimport {\n getComponentElement,\n getComponentElements,\n mountAnnounce,\n mountBackToTop,\n mountConsent,\n mountContent,\n mountDialog,\n mountHeader,\n mountHeaderTitle,\n mountPalette,\n mountProgress,\n mountSearch,\n mountSearchHiglight,\n mountSidebar,\n mountSource,\n mountTableOfContents,\n mountTabs,\n watchHeader,\n watchMain\n} from \"./components\"\nimport {\n SearchIndex,\n fetchSitemap,\n setupAlternate,\n setupClipboardJS,\n setupInstantNavigation,\n setupVersionSelector\n} from \"./integrations\"\nimport {\n patchEllipsis,\n patchIndeterminate,\n patchScrollfix,\n patchScrolllock\n} from \"./patches\"\nimport \"./polyfills\"\n\n/* ----------------------------------------------------------------------------\n * Functions - @todo refactor\n * ------------------------------------------------------------------------- */\n\n/**\n * Fetch search index\n *\n * @returns Search index observable\n */\nfunction fetchSearchIndex(): Observable {\n if (location.protocol === \"file:\") {\n return watchScript(\n `${new URL(\"search/search_index.js\", config.base)}`\n )\n .pipe(\n // @ts-ignore - @todo fix typings\n map(() => __index),\n shareReplay(1)\n )\n } else {\n return requestJSON(\n new URL(\"search/search_index.json\", config.base)\n )\n }\n}\n\n/* ----------------------------------------------------------------------------\n * Application\n * ------------------------------------------------------------------------- */\n\n/* Yay, JavaScript is available */\ndocument.documentElement.classList.remove(\"no-js\")\ndocument.documentElement.classList.add(\"js\")\n\n/* Set up navigation observables and subjects */\nconst document$ = watchDocument()\nconst location$ = watchLocation()\nconst target$ = watchLocationTarget(location$)\nconst keyboard$ = watchKeyboard()\n\n/* Set up media observables */\nconst viewport$ = watchViewport()\nconst tablet$ = watchMedia(\"(min-width: 60em)\")\nconst screen$ = watchMedia(\"(min-width: 76.25em)\")\nconst print$ = watchPrint()\n\n/* Retrieve search index, if search is enabled */\nconst config = configuration()\nconst index$ = document.forms.namedItem(\"search\")\n ? fetchSearchIndex()\n : NEVER\n\n/* Set up Clipboard.js integration */\nconst alert$ = new Subject()\nsetupClipboardJS({ alert$ })\n\n/* Set up language selector */\nsetupAlternate({ document$ })\n\n/* Set up progress indicator */\nconst progress$ = new Subject()\n\n/* Set up sitemap for instant navigation and previews */\nconst sitemap$ = fetchSitemap(config.base)\n\n/* Set up instant navigation, if enabled */\nif (feature(\"navigation.instant\"))\n setupInstantNavigation({ sitemap$, location$, viewport$, progress$ })\n .subscribe(document$)\n\n/* Set up version selector */\nif (config.version?.provider === \"mike\")\n setupVersionSelector({ document$ })\n\n/* Always close drawer and search on navigation */\nmerge(location$, target$)\n .pipe(\n delay(125)\n )\n .subscribe(() => {\n setToggle(\"drawer\", false)\n setToggle(\"search\", false)\n })\n\n/* Set up global keyboard handlers */\nkeyboard$\n .pipe(\n filter(({ mode }) => mode === \"global\")\n )\n .subscribe(key => {\n switch (key.type) {\n\n /* Go to previous page */\n case \"p\":\n case \",\":\n const prev = getOptionalElement(\"link[rel=prev]\")\n if (typeof prev !== \"undefined\")\n setLocation(prev)\n break\n\n /* Go to next page */\n case \"n\":\n case \".\":\n const next = getOptionalElement(\"link[rel=next]\")\n if (typeof next !== \"undefined\")\n setLocation(next)\n break\n\n /* Expand navigation, see https://bit.ly/3ZjG5io */\n case \"Enter\":\n const active = getActiveElement()\n if (active instanceof HTMLLabelElement)\n active.click()\n }\n })\n\n/* Set up patches */\npatchEllipsis({ viewport$, document$ })\npatchIndeterminate({ document$, tablet$ })\npatchScrollfix({ document$ })\npatchScrolllock({ viewport$, tablet$ })\n\n/* Set up header and main area observable */\nconst header$ = watchHeader(getComponentElement(\"header\"), { viewport$ })\nconst main$ = document$\n .pipe(\n map(() => getComponentElement(\"main\")),\n switchMap(el => watchMain(el, { viewport$, header$ })),\n shareReplay(1)\n )\n\n/* Set up control component observables */\nconst control$ = merge(\n\n /* Consent */\n ...getComponentElements(\"consent\")\n .map(el => mountConsent(el, { target$ })),\n\n /* Dialog */\n ...getComponentElements(\"dialog\")\n .map(el => mountDialog(el, { alert$ })),\n\n /* Color palette */\n ...getComponentElements(\"palette\")\n .map(el => mountPalette(el)),\n\n /* Progress bar */\n ...getComponentElements(\"progress\")\n .map(el => mountProgress(el, { progress$ })),\n\n /* Search */\n ...getComponentElements(\"search\")\n .map(el => mountSearch(el, { index$, keyboard$ })),\n\n /* Repository information */\n ...getComponentElements(\"source\")\n .map(el => mountSource(el))\n)\n\n/* Set up content component observables */\nconst content$ = defer(() => merge(\n\n /* Announcement bar */\n ...getComponentElements(\"announce\")\n .map(el => mountAnnounce(el)),\n\n /* Content */\n ...getComponentElements(\"content\")\n .map(el => mountContent(el, { sitemap$, viewport$, target$, print$ })),\n\n /* Search highlighting */\n ...getComponentElements(\"content\")\n .map(el => feature(\"search.highlight\")\n ? mountSearchHiglight(el, { index$, location$ })\n : EMPTY\n ),\n\n /* Header */\n ...getComponentElements(\"header\")\n .map(el => mountHeader(el, { viewport$, header$, main$ })),\n\n /* Header title */\n ...getComponentElements(\"header-title\")\n .map(el => mountHeaderTitle(el, { viewport$, header$ })),\n\n /* Sidebar */\n ...getComponentElements(\"sidebar\")\n .map(el => el.getAttribute(\"data-md-type\") === \"navigation\"\n ? at(screen$, () => mountSidebar(el, { viewport$, header$, main$ }))\n : at(tablet$, () => mountSidebar(el, { viewport$, header$, main$ }))\n ),\n\n /* Navigation tabs */\n ...getComponentElements(\"tabs\")\n .map(el => mountTabs(el, { viewport$, header$ })),\n\n /* Table of contents */\n ...getComponentElements(\"toc\")\n .map(el => mountTableOfContents(el, {\n viewport$, header$, main$, target$\n })),\n\n /* Back-to-top button */\n ...getComponentElements(\"top\")\n .map(el => mountBackToTop(el, { viewport$, header$, main$, target$ }))\n))\n\n/* Set up component observables */\nconst component$ = document$\n .pipe(\n switchMap(() => content$),\n mergeWith(control$),\n shareReplay(1)\n )\n\n/* Subscribe to all components */\ncomponent$.subscribe()\n\n/* ----------------------------------------------------------------------------\n * Exports\n * ------------------------------------------------------------------------- */\n\nwindow.document$ = document$ /* Document observable */\nwindow.location$ = location$ /* Location subject */\nwindow.target$ = target$ /* Location target observable */\nwindow.keyboard$ = keyboard$ /* Keyboard observable */\nwindow.viewport$ = viewport$ /* Viewport observable */\nwindow.tablet$ = tablet$ /* Media tablet observable */\nwindow.screen$ = screen$ /* Media screen observable */\nwindow.print$ = print$ /* Media print observable */\nwindow.alert$ = alert$ /* Alert subject */\nwindow.progress$ = progress$ /* Progress indicator subject */\nwindow.component$ = component$ /* Component observable */\n", "/******************************************************************************\nCopyright (c) Microsoft Corporation.\n\nPermission to use, copy, modify, and/or distribute this software for any\npurpose with or without fee is hereby granted.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\nREGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY\nAND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\nINDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\nLOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\nOTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\nPERFORMANCE OF THIS SOFTWARE.\n***************************************************************************** */\n/* global Reflect, Promise, SuppressedError, Symbol, Iterator */\n\nvar extendStatics = function(d, b) {\n extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };\n return extendStatics(d, b);\n};\n\nexport function __extends(d, b) {\n if (typeof b !== \"function\" && b !== null)\n throw new TypeError(\"Class extends value \" + String(b) + \" is not a constructor or null\");\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n}\n\nexport var __assign = function() {\n __assign = Object.assign || function __assign(t) {\n for (var s, i = 1, n = arguments.length; i < n; i++) {\n s = arguments[i];\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];\n }\n return t;\n }\n return __assign.apply(this, arguments);\n}\n\nexport function __rest(s, e) {\n var t = {};\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)\n t[p] = s[p];\n if (s != null && typeof Object.getOwnPropertySymbols === \"function\")\n for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {\n if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))\n t[p[i]] = s[p[i]];\n }\n return t;\n}\n\nexport function __decorate(decorators, target, key, desc) {\n var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;\n if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\") r = Reflect.decorate(decorators, target, key, desc);\n else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\n return c > 3 && r && Object.defineProperty(target, key, r), r;\n}\n\nexport function __param(paramIndex, decorator) {\n return function (target, key) { decorator(target, key, paramIndex); }\n}\n\nexport function __esDecorate(ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {\n function accept(f) { if (f !== void 0 && typeof f !== \"function\") throw new TypeError(\"Function expected\"); return f; }\n var kind = contextIn.kind, key = kind === \"getter\" ? \"get\" : kind === \"setter\" ? \"set\" : \"value\";\n var target = !descriptorIn && ctor ? contextIn[\"static\"] ? ctor : ctor.prototype : null;\n var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});\n var _, done = false;\n for (var i = decorators.length - 1; i >= 0; i--) {\n var context = {};\n for (var p in contextIn) context[p] = p === \"access\" ? {} : contextIn[p];\n for (var p in contextIn.access) context.access[p] = contextIn.access[p];\n context.addInitializer = function (f) { if (done) throw new TypeError(\"Cannot add initializers after decoration has completed\"); extraInitializers.push(accept(f || null)); };\n var result = (0, decorators[i])(kind === \"accessor\" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);\n if (kind === \"accessor\") {\n if (result === void 0) continue;\n if (result === null || typeof result !== \"object\") throw new TypeError(\"Object expected\");\n if (_ = accept(result.get)) descriptor.get = _;\n if (_ = accept(result.set)) descriptor.set = _;\n if (_ = accept(result.init)) initializers.unshift(_);\n }\n else if (_ = accept(result)) {\n if (kind === \"field\") initializers.unshift(_);\n else descriptor[key] = _;\n }\n }\n if (target) Object.defineProperty(target, contextIn.name, descriptor);\n done = true;\n};\n\nexport function __runInitializers(thisArg, initializers, value) {\n var useValue = arguments.length > 2;\n for (var i = 0; i < initializers.length; i++) {\n value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);\n }\n return useValue ? value : void 0;\n};\n\nexport function __propKey(x) {\n return typeof x === \"symbol\" ? x : \"\".concat(x);\n};\n\nexport function __setFunctionName(f, name, prefix) {\n if (typeof name === \"symbol\") name = name.description ? \"[\".concat(name.description, \"]\") : \"\";\n return Object.defineProperty(f, \"name\", { configurable: true, value: prefix ? \"\".concat(prefix, \" \", name) : name });\n};\n\nexport function __metadata(metadataKey, metadataValue) {\n if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\") return Reflect.metadata(metadataKey, metadataValue);\n}\n\nexport function __awaiter(thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n}\n\nexport function __generator(thisArg, body) {\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === \"function\" ? Iterator : Object).prototype);\n return g.next = verb(0), g[\"throw\"] = verb(1), g[\"return\"] = verb(2), typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\n function verb(n) { return function (v) { return step([n, v]); }; }\n function step(op) {\n if (f) throw new TypeError(\"Generator is already executing.\");\n while (g && (g = 0, op[0] && (_ = 0)), _) try {\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\n if (y = 0, t) op = [op[0] & 2, t.value];\n switch (op[0]) {\n case 0: case 1: t = op; break;\n case 4: _.label++; return { value: op[1], done: false };\n case 5: _.label++; y = op[1]; op = [0]; continue;\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\n default:\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\n if (t[2]) _.ops.pop();\n _.trys.pop(); continue;\n }\n op = body.call(thisArg, _);\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\n }\n}\n\nexport var __createBinding = Object.create ? (function(o, m, k, k2) {\n if (k2 === undefined) k2 = k;\n var desc = Object.getOwnPropertyDescriptor(m, k);\n if (!desc || (\"get\" in desc ? !m.__esModule : desc.writable || desc.configurable)) {\n desc = { enumerable: true, get: function() { return m[k]; } };\n }\n Object.defineProperty(o, k2, desc);\n}) : (function(o, m, k, k2) {\n if (k2 === undefined) k2 = k;\n o[k2] = m[k];\n});\n\nexport function __exportStar(m, o) {\n for (var p in m) if (p !== \"default\" && !Object.prototype.hasOwnProperty.call(o, p)) __createBinding(o, m, p);\n}\n\nexport function __values(o) {\n var s = typeof Symbol === \"function\" && Symbol.iterator, m = s && o[s], i = 0;\n if (m) return m.call(o);\n if (o && typeof o.length === \"number\") return {\n next: function () {\n if (o && i >= o.length) o = void 0;\n return { value: o && o[i++], done: !o };\n }\n };\n throw new TypeError(s ? \"Object is not iterable.\" : \"Symbol.iterator is not defined.\");\n}\n\nexport function __read(o, n) {\n var m = typeof Symbol === \"function\" && o[Symbol.iterator];\n if (!m) return o;\n var i = m.call(o), r, ar = [], e;\n try {\n while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);\n }\n catch (error) { e = { error: error }; }\n finally {\n try {\n if (r && !r.done && (m = i[\"return\"])) m.call(i);\n }\n finally { if (e) throw e.error; }\n }\n return ar;\n}\n\n/** @deprecated */\nexport function __spread() {\n for (var ar = [], i = 0; i < arguments.length; i++)\n ar = ar.concat(__read(arguments[i]));\n return ar;\n}\n\n/** @deprecated */\nexport function __spreadArrays() {\n for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;\n for (var r = Array(s), k = 0, i = 0; i < il; i++)\n for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)\n r[k] = a[j];\n return r;\n}\n\nexport function __spreadArray(to, from, pack) {\n if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {\n if (ar || !(i in from)) {\n if (!ar) ar = Array.prototype.slice.call(from, 0, i);\n ar[i] = from[i];\n }\n }\n return to.concat(ar || Array.prototype.slice.call(from));\n}\n\nexport function __await(v) {\n return this instanceof __await ? (this.v = v, this) : new __await(v);\n}\n\nexport function __asyncGenerator(thisArg, _arguments, generator) {\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\n var g = generator.apply(thisArg, _arguments || []), i, q = [];\n return i = Object.create((typeof AsyncIterator === \"function\" ? AsyncIterator : Object).prototype), verb(\"next\"), verb(\"throw\"), verb(\"return\", awaitReturn), i[Symbol.asyncIterator] = function () { return this; }, i;\n function awaitReturn(f) { return function (v) { return Promise.resolve(v).then(f, reject); }; }\n function verb(n, f) { if (g[n]) { i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; if (f) i[n] = f(i[n]); } }\n function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }\n function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }\n function fulfill(value) { resume(\"next\", value); }\n function reject(value) { resume(\"throw\", value); }\n function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }\n}\n\nexport function __asyncDelegator(o) {\n var i, p;\n return i = {}, verb(\"next\"), verb(\"throw\", function (e) { throw e; }), verb(\"return\"), i[Symbol.iterator] = function () { return this; }, i;\n function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: false } : f ? f(v) : v; } : f; }\n}\n\nexport function __asyncValues(o) {\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\n var m = o[Symbol.asyncIterator], i;\n return m ? m.call(o) : (o = typeof __values === \"function\" ? __values(o) : o[Symbol.iterator](), i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i);\n function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }\n function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }\n}\n\nexport function __makeTemplateObject(cooked, raw) {\n if (Object.defineProperty) { Object.defineProperty(cooked, \"raw\", { value: raw }); } else { cooked.raw = raw; }\n return cooked;\n};\n\nvar __setModuleDefault = Object.create ? (function(o, v) {\n Object.defineProperty(o, \"default\", { enumerable: true, value: v });\n}) : function(o, v) {\n o[\"default\"] = v;\n};\n\nexport function __importStar(mod) {\n if (mod && mod.__esModule) return mod;\n var result = {};\n if (mod != null) for (var k in mod) if (k !== \"default\" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);\n __setModuleDefault(result, mod);\n return result;\n}\n\nexport function __importDefault(mod) {\n return (mod && mod.__esModule) ? mod : { default: mod };\n}\n\nexport function __classPrivateFieldGet(receiver, state, kind, f) {\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a getter\");\n if (typeof state === \"function\" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError(\"Cannot read private member from an object whose class did not declare it\");\n return kind === \"m\" ? f : kind === \"a\" ? f.call(receiver) : f ? f.value : state.get(receiver);\n}\n\nexport function __classPrivateFieldSet(receiver, state, value, kind, f) {\n if (kind === \"m\") throw new TypeError(\"Private method is not writable\");\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a setter\");\n if (typeof state === \"function\" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError(\"Cannot write private member to an object whose class did not declare it\");\n return (kind === \"a\" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;\n}\n\nexport function __classPrivateFieldIn(state, receiver) {\n if (receiver === null || (typeof receiver !== \"object\" && typeof receiver !== \"function\")) throw new TypeError(\"Cannot use 'in' operator on non-object\");\n return typeof state === \"function\" ? receiver === state : state.has(receiver);\n}\n\nexport function __addDisposableResource(env, value, async) {\n if (value !== null && value !== void 0) {\n if (typeof value !== \"object\" && typeof value !== \"function\") throw new TypeError(\"Object expected.\");\n var dispose, inner;\n if (async) {\n if (!Symbol.asyncDispose) throw new TypeError(\"Symbol.asyncDispose is not defined.\");\n dispose = value[Symbol.asyncDispose];\n }\n if (dispose === void 0) {\n if (!Symbol.dispose) throw new TypeError(\"Symbol.dispose is not defined.\");\n dispose = value[Symbol.dispose];\n if (async) inner = dispose;\n }\n if (typeof dispose !== \"function\") throw new TypeError(\"Object not disposable.\");\n if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };\n env.stack.push({ value: value, dispose: dispose, async: async });\n }\n else if (async) {\n env.stack.push({ async: true });\n }\n return value;\n}\n\nvar _SuppressedError = typeof SuppressedError === \"function\" ? SuppressedError : function (error, suppressed, message) {\n var e = new Error(message);\n return e.name = \"SuppressedError\", e.error = error, e.suppressed = suppressed, e;\n};\n\nexport function __disposeResources(env) {\n function fail(e) {\n env.error = env.hasError ? new _SuppressedError(e, env.error, \"An error was suppressed during disposal.\") : e;\n env.hasError = true;\n }\n var r, s = 0;\n function next() {\n while (r = env.stack.pop()) {\n try {\n if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);\n if (r.dispose) {\n var result = r.dispose.call(r.value);\n if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });\n }\n else s |= 1;\n }\n catch (e) {\n fail(e);\n }\n }\n if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();\n if (env.hasError) throw env.error;\n }\n return next();\n}\n\nexport default {\n __extends,\n __assign,\n __rest,\n __decorate,\n __param,\n __metadata,\n __awaiter,\n __generator,\n __createBinding,\n __exportStar,\n __values,\n __read,\n __spread,\n __spreadArrays,\n __spreadArray,\n __await,\n __asyncGenerator,\n __asyncDelegator,\n __asyncValues,\n __makeTemplateObject,\n __importStar,\n __importDefault,\n __classPrivateFieldGet,\n __classPrivateFieldSet,\n __classPrivateFieldIn,\n __addDisposableResource,\n __disposeResources,\n};\n", "/**\n * Returns true if the object is a function.\n * @param value The value to check\n */\nexport function isFunction(value: any): value is (...args: any[]) => any {\n return typeof value === 'function';\n}\n", "/**\n * Used to create Error subclasses until the community moves away from ES5.\n *\n * This is because compiling from TypeScript down to ES5 has issues with subclassing Errors\n * as well as other built-in types: https://github.com/Microsoft/TypeScript/issues/12123\n *\n * @param createImpl A factory function to create the actual constructor implementation. The returned\n * function should be a named function that calls `_super` internally.\n */\nexport function createErrorClass(createImpl: (_super: any) => any): T {\n const _super = (instance: any) => {\n Error.call(instance);\n instance.stack = new Error().stack;\n };\n\n const ctorFunc = createImpl(_super);\n ctorFunc.prototype = Object.create(Error.prototype);\n ctorFunc.prototype.constructor = ctorFunc;\n return ctorFunc;\n}\n", "import { createErrorClass } from './createErrorClass';\n\nexport interface UnsubscriptionError extends Error {\n readonly errors: any[];\n}\n\nexport interface UnsubscriptionErrorCtor {\n /**\n * @deprecated Internal implementation detail. Do not construct error instances.\n * Cannot be tagged as internal: https://github.com/ReactiveX/rxjs/issues/6269\n */\n new (errors: any[]): UnsubscriptionError;\n}\n\n/**\n * An error thrown when one or more errors have occurred during the\n * `unsubscribe` of a {@link Subscription}.\n */\nexport const UnsubscriptionError: UnsubscriptionErrorCtor = createErrorClass(\n (_super) =>\n function UnsubscriptionErrorImpl(this: any, errors: (Error | string)[]) {\n _super(this);\n this.message = errors\n ? `${errors.length} errors occurred during unsubscription:\n${errors.map((err, i) => `${i + 1}) ${err.toString()}`).join('\\n ')}`\n : '';\n this.name = 'UnsubscriptionError';\n this.errors = errors;\n }\n);\n", "/**\n * Removes an item from an array, mutating it.\n * @param arr The array to remove the item from\n * @param item The item to remove\n */\nexport function arrRemove(arr: T[] | undefined | null, item: T) {\n if (arr) {\n const index = arr.indexOf(item);\n 0 <= index && arr.splice(index, 1);\n }\n}\n", "import { isFunction } from './util/isFunction';\nimport { UnsubscriptionError } from './util/UnsubscriptionError';\nimport { SubscriptionLike, TeardownLogic, Unsubscribable } from './types';\nimport { arrRemove } from './util/arrRemove';\n\n/**\n * Represents a disposable resource, such as the execution of an Observable. A\n * Subscription has one important method, `unsubscribe`, that takes no argument\n * and just disposes the resource held by the subscription.\n *\n * Additionally, subscriptions may be grouped together through the `add()`\n * method, which will attach a child Subscription to the current Subscription.\n * When a Subscription is unsubscribed, all its children (and its grandchildren)\n * will be unsubscribed as well.\n */\nexport class Subscription implements SubscriptionLike {\n public static EMPTY = (() => {\n const empty = new Subscription();\n empty.closed = true;\n return empty;\n })();\n\n /**\n * A flag to indicate whether this Subscription has already been unsubscribed.\n */\n public closed = false;\n\n private _parentage: Subscription[] | Subscription | null = null;\n\n /**\n * The list of registered finalizers to execute upon unsubscription. Adding and removing from this\n * list occurs in the {@link #add} and {@link #remove} methods.\n */\n private _finalizers: Exclude[] | null = null;\n\n /**\n * @param initialTeardown A function executed first as part of the finalization\n * process that is kicked off when {@link #unsubscribe} is called.\n */\n constructor(private initialTeardown?: () => void) {}\n\n /**\n * Disposes the resources held by the subscription. May, for instance, cancel\n * an ongoing Observable execution or cancel any other type of work that\n * started when the Subscription was created.\n */\n unsubscribe(): void {\n let errors: any[] | undefined;\n\n if (!this.closed) {\n this.closed = true;\n\n // Remove this from it's parents.\n const { _parentage } = this;\n if (_parentage) {\n this._parentage = null;\n if (Array.isArray(_parentage)) {\n for (const parent of _parentage) {\n parent.remove(this);\n }\n } else {\n _parentage.remove(this);\n }\n }\n\n const { initialTeardown: initialFinalizer } = this;\n if (isFunction(initialFinalizer)) {\n try {\n initialFinalizer();\n } catch (e) {\n errors = e instanceof UnsubscriptionError ? e.errors : [e];\n }\n }\n\n const { _finalizers } = this;\n if (_finalizers) {\n this._finalizers = null;\n for (const finalizer of _finalizers) {\n try {\n execFinalizer(finalizer);\n } catch (err) {\n errors = errors ?? [];\n if (err instanceof UnsubscriptionError) {\n errors = [...errors, ...err.errors];\n } else {\n errors.push(err);\n }\n }\n }\n }\n\n if (errors) {\n throw new UnsubscriptionError(errors);\n }\n }\n }\n\n /**\n * Adds a finalizer to this subscription, so that finalization will be unsubscribed/called\n * when this subscription is unsubscribed. If this subscription is already {@link #closed},\n * because it has already been unsubscribed, then whatever finalizer is passed to it\n * will automatically be executed (unless the finalizer itself is also a closed subscription).\n *\n * Closed Subscriptions cannot be added as finalizers to any subscription. Adding a closed\n * subscription to a any subscription will result in no operation. (A noop).\n *\n * Adding a subscription to itself, or adding `null` or `undefined` will not perform any\n * operation at all. (A noop).\n *\n * `Subscription` instances that are added to this instance will automatically remove themselves\n * if they are unsubscribed. Functions and {@link Unsubscribable} objects that you wish to remove\n * will need to be removed manually with {@link #remove}\n *\n * @param teardown The finalization logic to add to this subscription.\n */\n add(teardown: TeardownLogic): void {\n // Only add the finalizer if it's not undefined\n // and don't add a subscription to itself.\n if (teardown && teardown !== this) {\n if (this.closed) {\n // If this subscription is already closed,\n // execute whatever finalizer is handed to it automatically.\n execFinalizer(teardown);\n } else {\n if (teardown instanceof Subscription) {\n // We don't add closed subscriptions, and we don't add the same subscription\n // twice. Subscription unsubscribe is idempotent.\n if (teardown.closed || teardown._hasParent(this)) {\n return;\n }\n teardown._addParent(this);\n }\n (this._finalizers = this._finalizers ?? []).push(teardown);\n }\n }\n }\n\n /**\n * Checks to see if a this subscription already has a particular parent.\n * This will signal that this subscription has already been added to the parent in question.\n * @param parent the parent to check for\n */\n private _hasParent(parent: Subscription) {\n const { _parentage } = this;\n return _parentage === parent || (Array.isArray(_parentage) && _parentage.includes(parent));\n }\n\n /**\n * Adds a parent to this subscription so it can be removed from the parent if it\n * unsubscribes on it's own.\n *\n * NOTE: THIS ASSUMES THAT {@link _hasParent} HAS ALREADY BEEN CHECKED.\n * @param parent The parent subscription to add\n */\n private _addParent(parent: Subscription) {\n const { _parentage } = this;\n this._parentage = Array.isArray(_parentage) ? (_parentage.push(parent), _parentage) : _parentage ? [_parentage, parent] : parent;\n }\n\n /**\n * Called on a child when it is removed via {@link #remove}.\n * @param parent The parent to remove\n */\n private _removeParent(parent: Subscription) {\n const { _parentage } = this;\n if (_parentage === parent) {\n this._parentage = null;\n } else if (Array.isArray(_parentage)) {\n arrRemove(_parentage, parent);\n }\n }\n\n /**\n * Removes a finalizer from this subscription that was previously added with the {@link #add} method.\n *\n * Note that `Subscription` instances, when unsubscribed, will automatically remove themselves\n * from every other `Subscription` they have been added to. This means that using the `remove` method\n * is not a common thing and should be used thoughtfully.\n *\n * If you add the same finalizer instance of a function or an unsubscribable object to a `Subscription` instance\n * more than once, you will need to call `remove` the same number of times to remove all instances.\n *\n * All finalizer instances are removed to free up memory upon unsubscription.\n *\n * @param teardown The finalizer to remove from this subscription\n */\n remove(teardown: Exclude): void {\n const { _finalizers } = this;\n _finalizers && arrRemove(_finalizers, teardown);\n\n if (teardown instanceof Subscription) {\n teardown._removeParent(this);\n }\n }\n}\n\nexport const EMPTY_SUBSCRIPTION = Subscription.EMPTY;\n\nexport function isSubscription(value: any): value is Subscription {\n return (\n value instanceof Subscription ||\n (value && 'closed' in value && isFunction(value.remove) && isFunction(value.add) && isFunction(value.unsubscribe))\n );\n}\n\nfunction execFinalizer(finalizer: Unsubscribable | (() => void)) {\n if (isFunction(finalizer)) {\n finalizer();\n } else {\n finalizer.unsubscribe();\n }\n}\n", "import { Subscriber } from './Subscriber';\nimport { ObservableNotification } from './types';\n\n/**\n * The {@link GlobalConfig} object for RxJS. It is used to configure things\n * like how to react on unhandled errors.\n */\nexport const config: GlobalConfig = {\n onUnhandledError: null,\n onStoppedNotification: null,\n Promise: undefined,\n useDeprecatedSynchronousErrorHandling: false,\n useDeprecatedNextContext: false,\n};\n\n/**\n * The global configuration object for RxJS, used to configure things\n * like how to react on unhandled errors. Accessible via {@link config}\n * object.\n */\nexport interface GlobalConfig {\n /**\n * A registration point for unhandled errors from RxJS. These are errors that\n * cannot were not handled by consuming code in the usual subscription path. For\n * example, if you have this configured, and you subscribe to an observable without\n * providing an error handler, errors from that subscription will end up here. This\n * will _always_ be called asynchronously on another job in the runtime. This is because\n * we do not want errors thrown in this user-configured handler to interfere with the\n * behavior of the library.\n */\n onUnhandledError: ((err: any) => void) | null;\n\n /**\n * A registration point for notifications that cannot be sent to subscribers because they\n * have completed, errored or have been explicitly unsubscribed. By default, next, complete\n * and error notifications sent to stopped subscribers are noops. However, sometimes callers\n * might want a different behavior. For example, with sources that attempt to report errors\n * to stopped subscribers, a caller can configure RxJS to throw an unhandled error instead.\n * This will _always_ be called asynchronously on another job in the runtime. This is because\n * we do not want errors thrown in this user-configured handler to interfere with the\n * behavior of the library.\n */\n onStoppedNotification: ((notification: ObservableNotification, subscriber: Subscriber) => void) | null;\n\n /**\n * The promise constructor used by default for {@link Observable#toPromise toPromise} and {@link Observable#forEach forEach}\n * methods.\n *\n * @deprecated As of version 8, RxJS will no longer support this sort of injection of a\n * Promise constructor. If you need a Promise implementation other than native promises,\n * please polyfill/patch Promise as you see appropriate. Will be removed in v8.\n */\n Promise?: PromiseConstructorLike;\n\n /**\n * If true, turns on synchronous error rethrowing, which is a deprecated behavior\n * in v6 and higher. This behavior enables bad patterns like wrapping a subscribe\n * call in a try/catch block. It also enables producer interference, a nasty bug\n * where a multicast can be broken for all observers by a downstream consumer with\n * an unhandled error. DO NOT USE THIS FLAG UNLESS IT'S NEEDED TO BUY TIME\n * FOR MIGRATION REASONS.\n *\n * @deprecated As of version 8, RxJS will no longer support synchronous throwing\n * of unhandled errors. All errors will be thrown on a separate call stack to prevent bad\n * behaviors described above. Will be removed in v8.\n */\n useDeprecatedSynchronousErrorHandling: boolean;\n\n /**\n * If true, enables an as-of-yet undocumented feature from v5: The ability to access\n * `unsubscribe()` via `this` context in `next` functions created in observers passed\n * to `subscribe`.\n *\n * This is being removed because the performance was severely problematic, and it could also cause\n * issues when types other than POJOs are passed to subscribe as subscribers, as they will likely have\n * their `this` context overwritten.\n *\n * @deprecated As of version 8, RxJS will no longer support altering the\n * context of next functions provided as part of an observer to Subscribe. Instead,\n * you will have access to a subscription or a signal or token that will allow you to do things like\n * unsubscribe and test closed status. Will be removed in v8.\n */\n useDeprecatedNextContext: boolean;\n}\n", "import type { TimerHandle } from './timerHandle';\ntype SetTimeoutFunction = (handler: () => void, timeout?: number, ...args: any[]) => TimerHandle;\ntype ClearTimeoutFunction = (handle: TimerHandle) => void;\n\ninterface TimeoutProvider {\n setTimeout: SetTimeoutFunction;\n clearTimeout: ClearTimeoutFunction;\n delegate:\n | {\n setTimeout: SetTimeoutFunction;\n clearTimeout: ClearTimeoutFunction;\n }\n | undefined;\n}\n\nexport const timeoutProvider: TimeoutProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n setTimeout(handler: () => void, timeout?: number, ...args) {\n const { delegate } = timeoutProvider;\n if (delegate?.setTimeout) {\n return delegate.setTimeout(handler, timeout, ...args);\n }\n return setTimeout(handler, timeout, ...args);\n },\n clearTimeout(handle) {\n const { delegate } = timeoutProvider;\n return (delegate?.clearTimeout || clearTimeout)(handle as any);\n },\n delegate: undefined,\n};\n", "import { config } from '../config';\nimport { timeoutProvider } from '../scheduler/timeoutProvider';\n\n/**\n * Handles an error on another job either with the user-configured {@link onUnhandledError},\n * or by throwing it on that new job so it can be picked up by `window.onerror`, `process.on('error')`, etc.\n *\n * This should be called whenever there is an error that is out-of-band with the subscription\n * or when an error hits a terminal boundary of the subscription and no error handler was provided.\n *\n * @param err the error to report\n */\nexport function reportUnhandledError(err: any) {\n timeoutProvider.setTimeout(() => {\n const { onUnhandledError } = config;\n if (onUnhandledError) {\n // Execute the user-configured error handler.\n onUnhandledError(err);\n } else {\n // Throw so it is picked up by the runtime's uncaught error mechanism.\n throw err;\n }\n });\n}\n", "/* tslint:disable:no-empty */\nexport function noop() { }\n", "import { CompleteNotification, NextNotification, ErrorNotification } from './types';\n\n/**\n * A completion object optimized for memory use and created to be the\n * same \"shape\" as other notifications in v8.\n * @internal\n */\nexport const COMPLETE_NOTIFICATION = (() => createNotification('C', undefined, undefined) as CompleteNotification)();\n\n/**\n * Internal use only. Creates an optimized error notification that is the same \"shape\"\n * as other notifications.\n * @internal\n */\nexport function errorNotification(error: any): ErrorNotification {\n return createNotification('E', undefined, error) as any;\n}\n\n/**\n * Internal use only. Creates an optimized next notification that is the same \"shape\"\n * as other notifications.\n * @internal\n */\nexport function nextNotification(value: T) {\n return createNotification('N', value, undefined) as NextNotification;\n}\n\n/**\n * Ensures that all notifications created internally have the same \"shape\" in v8.\n *\n * TODO: This is only exported to support a crazy legacy test in `groupBy`.\n * @internal\n */\nexport function createNotification(kind: 'N' | 'E' | 'C', value: any, error: any) {\n return {\n kind,\n value,\n error,\n };\n}\n", "import { config } from '../config';\n\nlet context: { errorThrown: boolean; error: any } | null = null;\n\n/**\n * Handles dealing with errors for super-gross mode. Creates a context, in which\n * any synchronously thrown errors will be passed to {@link captureError}. Which\n * will record the error such that it will be rethrown after the call back is complete.\n * TODO: Remove in v8\n * @param cb An immediately executed function.\n */\nexport function errorContext(cb: () => void) {\n if (config.useDeprecatedSynchronousErrorHandling) {\n const isRoot = !context;\n if (isRoot) {\n context = { errorThrown: false, error: null };\n }\n cb();\n if (isRoot) {\n const { errorThrown, error } = context!;\n context = null;\n if (errorThrown) {\n throw error;\n }\n }\n } else {\n // This is the general non-deprecated path for everyone that\n // isn't crazy enough to use super-gross mode (useDeprecatedSynchronousErrorHandling)\n cb();\n }\n}\n\n/**\n * Captures errors only in super-gross mode.\n * @param err the error to capture\n */\nexport function captureError(err: any) {\n if (config.useDeprecatedSynchronousErrorHandling && context) {\n context.errorThrown = true;\n context.error = err;\n }\n}\n", "import { isFunction } from './util/isFunction';\nimport { Observer, ObservableNotification } from './types';\nimport { isSubscription, Subscription } from './Subscription';\nimport { config } from './config';\nimport { reportUnhandledError } from './util/reportUnhandledError';\nimport { noop } from './util/noop';\nimport { nextNotification, errorNotification, COMPLETE_NOTIFICATION } from './NotificationFactories';\nimport { timeoutProvider } from './scheduler/timeoutProvider';\nimport { captureError } from './util/errorContext';\n\n/**\n * Implements the {@link Observer} interface and extends the\n * {@link Subscription} class. While the {@link Observer} is the public API for\n * consuming the values of an {@link Observable}, all Observers get converted to\n * a Subscriber, in order to provide Subscription-like capabilities such as\n * `unsubscribe`. Subscriber is a common type in RxJS, and crucial for\n * implementing operators, but it is rarely used as a public API.\n */\nexport class Subscriber extends Subscription implements Observer {\n /**\n * A static factory for a Subscriber, given a (potentially partial) definition\n * of an Observer.\n * @param next The `next` callback of an Observer.\n * @param error The `error` callback of an\n * Observer.\n * @param complete The `complete` callback of an\n * Observer.\n * @return A Subscriber wrapping the (partially defined)\n * Observer represented by the given arguments.\n * @deprecated Do not use. Will be removed in v8. There is no replacement for this\n * method, and there is no reason to be creating instances of `Subscriber` directly.\n * If you have a specific use case, please file an issue.\n */\n static create(next?: (x?: T) => void, error?: (e?: any) => void, complete?: () => void): Subscriber {\n return new SafeSubscriber(next, error, complete);\n }\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n protected isStopped: boolean = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n protected destination: Subscriber | Observer; // this `any` is the escape hatch to erase extra type param (e.g. R)\n\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n * There is no reason to directly create an instance of Subscriber. This type is exported for typings reasons.\n */\n constructor(destination?: Subscriber | Observer) {\n super();\n if (destination) {\n this.destination = destination;\n // Automatically chain subscriptions together here.\n // if destination is a Subscription, then it is a Subscriber.\n if (isSubscription(destination)) {\n destination.add(this);\n }\n } else {\n this.destination = EMPTY_OBSERVER;\n }\n }\n\n /**\n * The {@link Observer} callback to receive notifications of type `next` from\n * the Observable, with a value. The Observable may call this method 0 or more\n * times.\n * @param value The `next` value.\n */\n next(value: T): void {\n if (this.isStopped) {\n handleStoppedNotification(nextNotification(value), this);\n } else {\n this._next(value!);\n }\n }\n\n /**\n * The {@link Observer} callback to receive notifications of type `error` from\n * the Observable, with an attached `Error`. Notifies the Observer that\n * the Observable has experienced an error condition.\n * @param err The `error` exception.\n */\n error(err?: any): void {\n if (this.isStopped) {\n handleStoppedNotification(errorNotification(err), this);\n } else {\n this.isStopped = true;\n this._error(err);\n }\n }\n\n /**\n * The {@link Observer} callback to receive a valueless notification of type\n * `complete` from the Observable. Notifies the Observer that the Observable\n * has finished sending push-based notifications.\n */\n complete(): void {\n if (this.isStopped) {\n handleStoppedNotification(COMPLETE_NOTIFICATION, this);\n } else {\n this.isStopped = true;\n this._complete();\n }\n }\n\n unsubscribe(): void {\n if (!this.closed) {\n this.isStopped = true;\n super.unsubscribe();\n this.destination = null!;\n }\n }\n\n protected _next(value: T): void {\n this.destination.next(value);\n }\n\n protected _error(err: any): void {\n try {\n this.destination.error(err);\n } finally {\n this.unsubscribe();\n }\n }\n\n protected _complete(): void {\n try {\n this.destination.complete();\n } finally {\n this.unsubscribe();\n }\n }\n}\n\n/**\n * This bind is captured here because we want to be able to have\n * compatibility with monoid libraries that tend to use a method named\n * `bind`. In particular, a library called Monio requires this.\n */\nconst _bind = Function.prototype.bind;\n\nfunction bind any>(fn: Fn, thisArg: any): Fn {\n return _bind.call(fn, thisArg);\n}\n\n/**\n * Internal optimization only, DO NOT EXPOSE.\n * @internal\n */\nclass ConsumerObserver implements Observer {\n constructor(private partialObserver: Partial>) {}\n\n next(value: T): void {\n const { partialObserver } = this;\n if (partialObserver.next) {\n try {\n partialObserver.next(value);\n } catch (error) {\n handleUnhandledError(error);\n }\n }\n }\n\n error(err: any): void {\n const { partialObserver } = this;\n if (partialObserver.error) {\n try {\n partialObserver.error(err);\n } catch (error) {\n handleUnhandledError(error);\n }\n } else {\n handleUnhandledError(err);\n }\n }\n\n complete(): void {\n const { partialObserver } = this;\n if (partialObserver.complete) {\n try {\n partialObserver.complete();\n } catch (error) {\n handleUnhandledError(error);\n }\n }\n }\n}\n\nexport class SafeSubscriber extends Subscriber {\n constructor(\n observerOrNext?: Partial> | ((value: T) => void) | null,\n error?: ((e?: any) => void) | null,\n complete?: (() => void) | null\n ) {\n super();\n\n let partialObserver: Partial>;\n if (isFunction(observerOrNext) || !observerOrNext) {\n // The first argument is a function, not an observer. The next\n // two arguments *could* be observers, or they could be empty.\n partialObserver = {\n next: (observerOrNext ?? undefined) as ((value: T) => void) | undefined,\n error: error ?? undefined,\n complete: complete ?? undefined,\n };\n } else {\n // The first argument is a partial observer.\n let context: any;\n if (this && config.useDeprecatedNextContext) {\n // This is a deprecated path that made `this.unsubscribe()` available in\n // next handler functions passed to subscribe. This only exists behind a flag\n // now, as it is *very* slow.\n context = Object.create(observerOrNext);\n context.unsubscribe = () => this.unsubscribe();\n partialObserver = {\n next: observerOrNext.next && bind(observerOrNext.next, context),\n error: observerOrNext.error && bind(observerOrNext.error, context),\n complete: observerOrNext.complete && bind(observerOrNext.complete, context),\n };\n } else {\n // The \"normal\" path. Just use the partial observer directly.\n partialObserver = observerOrNext;\n }\n }\n\n // Wrap the partial observer to ensure it's a full observer, and\n // make sure proper error handling is accounted for.\n this.destination = new ConsumerObserver(partialObserver);\n }\n}\n\nfunction handleUnhandledError(error: any) {\n if (config.useDeprecatedSynchronousErrorHandling) {\n captureError(error);\n } else {\n // Ideal path, we report this as an unhandled error,\n // which is thrown on a new call stack.\n reportUnhandledError(error);\n }\n}\n\n/**\n * An error handler used when no error handler was supplied\n * to the SafeSubscriber -- meaning no error handler was supplied\n * do the `subscribe` call on our observable.\n * @param err The error to handle\n */\nfunction defaultErrorHandler(err: any) {\n throw err;\n}\n\n/**\n * A handler for notifications that cannot be sent to a stopped subscriber.\n * @param notification The notification being sent.\n * @param subscriber The stopped subscriber.\n */\nfunction handleStoppedNotification(notification: ObservableNotification, subscriber: Subscriber) {\n const { onStoppedNotification } = config;\n onStoppedNotification && timeoutProvider.setTimeout(() => onStoppedNotification(notification, subscriber));\n}\n\n/**\n * The observer used as a stub for subscriptions where the user did not\n * pass any arguments to `subscribe`. Comes with the default error handling\n * behavior.\n */\nexport const EMPTY_OBSERVER: Readonly> & { closed: true } = {\n closed: true,\n next: noop,\n error: defaultErrorHandler,\n complete: noop,\n};\n", "/**\n * Symbol.observable or a string \"@@observable\". Used for interop\n *\n * @deprecated We will no longer be exporting this symbol in upcoming versions of RxJS.\n * Instead polyfill and use Symbol.observable directly *or* use https://www.npmjs.com/package/symbol-observable\n */\nexport const observable: string | symbol = (() => (typeof Symbol === 'function' && Symbol.observable) || '@@observable')();\n", "/**\n * This function takes one parameter and just returns it. Simply put,\n * this is like `(x: T): T => x`.\n *\n * ## Examples\n *\n * This is useful in some cases when using things like `mergeMap`\n *\n * ```ts\n * import { interval, take, map, range, mergeMap, identity } from 'rxjs';\n *\n * const source$ = interval(1000).pipe(take(5));\n *\n * const result$ = source$.pipe(\n * map(i => range(i)),\n * mergeMap(identity) // same as mergeMap(x => x)\n * );\n *\n * result$.subscribe({\n * next: console.log\n * });\n * ```\n *\n * Or when you want to selectively apply an operator\n *\n * ```ts\n * import { interval, take, identity } from 'rxjs';\n *\n * const shouldLimit = () => Math.random() < 0.5;\n *\n * const source$ = interval(1000);\n *\n * const result$ = source$.pipe(shouldLimit() ? take(5) : identity);\n *\n * result$.subscribe({\n * next: console.log\n * });\n * ```\n *\n * @param x Any value that is returned by this function\n * @returns The value passed as the first parameter to this function\n */\nexport function identity(x: T): T {\n return x;\n}\n", "import { identity } from './identity';\nimport { UnaryFunction } from '../types';\n\nexport function pipe(): typeof identity;\nexport function pipe(fn1: UnaryFunction): UnaryFunction;\nexport function pipe(fn1: UnaryFunction, fn2: UnaryFunction): UnaryFunction;\nexport function pipe(fn1: UnaryFunction, fn2: UnaryFunction, fn3: UnaryFunction): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction,\n fn9: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction,\n fn9: UnaryFunction,\n ...fns: UnaryFunction[]\n): UnaryFunction;\n\n/**\n * pipe() can be called on one or more functions, each of which can take one argument (\"UnaryFunction\")\n * and uses it to return a value.\n * It returns a function that takes one argument, passes it to the first UnaryFunction, and then\n * passes the result to the next one, passes that result to the next one, and so on. \n */\nexport function pipe(...fns: Array>): UnaryFunction {\n return pipeFromArray(fns);\n}\n\n/** @internal */\nexport function pipeFromArray(fns: Array>): UnaryFunction {\n if (fns.length === 0) {\n return identity as UnaryFunction;\n }\n\n if (fns.length === 1) {\n return fns[0];\n }\n\n return function piped(input: T): R {\n return fns.reduce((prev: any, fn: UnaryFunction) => fn(prev), input as any);\n };\n}\n", "import { Operator } from './Operator';\nimport { SafeSubscriber, Subscriber } from './Subscriber';\nimport { isSubscription, Subscription } from './Subscription';\nimport { TeardownLogic, OperatorFunction, Subscribable, Observer } from './types';\nimport { observable as Symbol_observable } from './symbol/observable';\nimport { pipeFromArray } from './util/pipe';\nimport { config } from './config';\nimport { isFunction } from './util/isFunction';\nimport { errorContext } from './util/errorContext';\n\n/**\n * A representation of any set of values over any amount of time. This is the most basic building block\n * of RxJS.\n */\nexport class Observable implements Subscribable {\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n */\n source: Observable | undefined;\n\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n */\n operator: Operator | undefined;\n\n /**\n * @param subscribe The function that is called when the Observable is\n * initially subscribed to. This function is given a Subscriber, to which new values\n * can be `next`ed, or an `error` method can be called to raise an error, or\n * `complete` can be called to notify of a successful completion.\n */\n constructor(subscribe?: (this: Observable, subscriber: Subscriber) => TeardownLogic) {\n if (subscribe) {\n this._subscribe = subscribe;\n }\n }\n\n // HACK: Since TypeScript inherits static properties too, we have to\n // fight against TypeScript here so Subject can have a different static create signature\n /**\n * Creates a new Observable by calling the Observable constructor\n * @param subscribe the subscriber function to be passed to the Observable constructor\n * @return A new observable.\n * @deprecated Use `new Observable()` instead. Will be removed in v8.\n */\n static create: (...args: any[]) => any = (subscribe?: (subscriber: Subscriber) => TeardownLogic) => {\n return new Observable(subscribe);\n };\n\n /**\n * Creates a new Observable, with this Observable instance as the source, and the passed\n * operator defined as the new observable's operator.\n * @param operator the operator defining the operation to take on the observable\n * @return A new observable with the Operator applied.\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n * If you have implemented an operator using `lift`, it is recommended that you create an\n * operator by simply returning `new Observable()` directly. See \"Creating new operators from\n * scratch\" section here: https://rxjs.dev/guide/operators\n */\n lift(operator?: Operator): Observable {\n const observable = new Observable();\n observable.source = this;\n observable.operator = operator;\n return observable;\n }\n\n subscribe(observerOrNext?: Partial> | ((value: T) => void)): Subscription;\n /** @deprecated Instead of passing separate callback arguments, use an observer argument. Signatures taking separate callback arguments will be removed in v8. Details: https://rxjs.dev/deprecations/subscribe-arguments */\n subscribe(next?: ((value: T) => void) | null, error?: ((error: any) => void) | null, complete?: (() => void) | null): Subscription;\n /**\n * Invokes an execution of an Observable and registers Observer handlers for notifications it will emit.\n *\n * Use it when you have all these Observables, but still nothing is happening.\n *\n * `subscribe` is not a regular operator, but a method that calls Observable's internal `subscribe` function. It\n * might be for example a function that you passed to Observable's constructor, but most of the time it is\n * a library implementation, which defines what will be emitted by an Observable, and when it be will emitted. This means\n * that calling `subscribe` is actually the moment when Observable starts its work, not when it is created, as it is often\n * the thought.\n *\n * Apart from starting the execution of an Observable, this method allows you to listen for values\n * that an Observable emits, as well as for when it completes or errors. You can achieve this in two\n * of the following ways.\n *\n * The first way is creating an object that implements {@link Observer} interface. It should have methods\n * defined by that interface, but note that it should be just a regular JavaScript object, which you can create\n * yourself in any way you want (ES6 class, classic function constructor, object literal etc.). In particular, do\n * not attempt to use any RxJS implementation details to create Observers - you don't need them. Remember also\n * that your object does not have to implement all methods. If you find yourself creating a method that doesn't\n * do anything, you can simply omit it. Note however, if the `error` method is not provided and an error happens,\n * it will be thrown asynchronously. Errors thrown asynchronously cannot be caught using `try`/`catch`. Instead,\n * use the {@link onUnhandledError} configuration option or use a runtime handler (like `window.onerror` or\n * `process.on('error)`) to be notified of unhandled errors. Because of this, it's recommended that you provide\n * an `error` method to avoid missing thrown errors.\n *\n * The second way is to give up on Observer object altogether and simply provide callback functions in place of its methods.\n * This means you can provide three functions as arguments to `subscribe`, where the first function is equivalent\n * of a `next` method, the second of an `error` method and the third of a `complete` method. Just as in case of an Observer,\n * if you do not need to listen for something, you can omit a function by passing `undefined` or `null`,\n * since `subscribe` recognizes these functions by where they were placed in function call. When it comes\n * to the `error` function, as with an Observer, if not provided, errors emitted by an Observable will be thrown asynchronously.\n *\n * You can, however, subscribe with no parameters at all. This may be the case where you're not interested in terminal events\n * and you also handled emissions internally by using operators (e.g. using `tap`).\n *\n * Whichever style of calling `subscribe` you use, in both cases it returns a Subscription object.\n * This object allows you to call `unsubscribe` on it, which in turn will stop the work that an Observable does and will clean\n * up all resources that an Observable used. Note that cancelling a subscription will not call `complete` callback\n * provided to `subscribe` function, which is reserved for a regular completion signal that comes from an Observable.\n *\n * Remember that callbacks provided to `subscribe` are not guaranteed to be called asynchronously.\n * It is an Observable itself that decides when these functions will be called. For example {@link of}\n * by default emits all its values synchronously. Always check documentation for how given Observable\n * will behave when subscribed and if its default behavior can be modified with a `scheduler`.\n *\n * #### Examples\n *\n * Subscribe with an {@link guide/observer Observer}\n *\n * ```ts\n * import { of } from 'rxjs';\n *\n * const sumObserver = {\n * sum: 0,\n * next(value) {\n * console.log('Adding: ' + value);\n * this.sum = this.sum + value;\n * },\n * error() {\n * // We actually could just remove this method,\n * // since we do not really care about errors right now.\n * },\n * complete() {\n * console.log('Sum equals: ' + this.sum);\n * }\n * };\n *\n * of(1, 2, 3) // Synchronously emits 1, 2, 3 and then completes.\n * .subscribe(sumObserver);\n *\n * // Logs:\n * // 'Adding: 1'\n * // 'Adding: 2'\n * // 'Adding: 3'\n * // 'Sum equals: 6'\n * ```\n *\n * Subscribe with functions ({@link deprecations/subscribe-arguments deprecated})\n *\n * ```ts\n * import { of } from 'rxjs'\n *\n * let sum = 0;\n *\n * of(1, 2, 3).subscribe(\n * value => {\n * console.log('Adding: ' + value);\n * sum = sum + value;\n * },\n * undefined,\n * () => console.log('Sum equals: ' + sum)\n * );\n *\n * // Logs:\n * // 'Adding: 1'\n * // 'Adding: 2'\n * // 'Adding: 3'\n * // 'Sum equals: 6'\n * ```\n *\n * Cancel a subscription\n *\n * ```ts\n * import { interval } from 'rxjs';\n *\n * const subscription = interval(1000).subscribe({\n * next(num) {\n * console.log(num)\n * },\n * complete() {\n * // Will not be called, even when cancelling subscription.\n * console.log('completed!');\n * }\n * });\n *\n * setTimeout(() => {\n * subscription.unsubscribe();\n * console.log('unsubscribed!');\n * }, 2500);\n *\n * // Logs:\n * // 0 after 1s\n * // 1 after 2s\n * // 'unsubscribed!' after 2.5s\n * ```\n *\n * @param observerOrNext Either an {@link Observer} with some or all callback methods,\n * or the `next` handler that is called for each value emitted from the subscribed Observable.\n * @param error A handler for a terminal event resulting from an error. If no error handler is provided,\n * the error will be thrown asynchronously as unhandled.\n * @param complete A handler for a terminal event resulting from successful completion.\n * @return A subscription reference to the registered handlers.\n */\n subscribe(\n observerOrNext?: Partial> | ((value: T) => void) | null,\n error?: ((error: any) => void) | null,\n complete?: (() => void) | null\n ): Subscription {\n const subscriber = isSubscriber(observerOrNext) ? observerOrNext : new SafeSubscriber(observerOrNext, error, complete);\n\n errorContext(() => {\n const { operator, source } = this;\n subscriber.add(\n operator\n ? // We're dealing with a subscription in the\n // operator chain to one of our lifted operators.\n operator.call(subscriber, source)\n : source\n ? // If `source` has a value, but `operator` does not, something that\n // had intimate knowledge of our API, like our `Subject`, must have\n // set it. We're going to just call `_subscribe` directly.\n this._subscribe(subscriber)\n : // In all other cases, we're likely wrapping a user-provided initializer\n // function, so we need to catch errors and handle them appropriately.\n this._trySubscribe(subscriber)\n );\n });\n\n return subscriber;\n }\n\n /** @internal */\n protected _trySubscribe(sink: Subscriber): TeardownLogic {\n try {\n return this._subscribe(sink);\n } catch (err) {\n // We don't need to return anything in this case,\n // because it's just going to try to `add()` to a subscription\n // above.\n sink.error(err);\n }\n }\n\n /**\n * Used as a NON-CANCELLABLE means of subscribing to an observable, for use with\n * APIs that expect promises, like `async/await`. You cannot unsubscribe from this.\n *\n * **WARNING**: Only use this with observables you *know* will complete. If the source\n * observable does not complete, you will end up with a promise that is hung up, and\n * potentially all of the state of an async function hanging out in memory. To avoid\n * this situation, look into adding something like {@link timeout}, {@link take},\n * {@link takeWhile}, or {@link takeUntil} amongst others.\n *\n * #### Example\n *\n * ```ts\n * import { interval, take } from 'rxjs';\n *\n * const source$ = interval(1000).pipe(take(4));\n *\n * async function getTotal() {\n * let total = 0;\n *\n * await source$.forEach(value => {\n * total += value;\n * console.log('observable -> ' + value);\n * });\n *\n * return total;\n * }\n *\n * getTotal().then(\n * total => console.log('Total: ' + total)\n * );\n *\n * // Expected:\n * // 'observable -> 0'\n * // 'observable -> 1'\n * // 'observable -> 2'\n * // 'observable -> 3'\n * // 'Total: 6'\n * ```\n *\n * @param next A handler for each value emitted by the observable.\n * @return A promise that either resolves on observable completion or\n * rejects with the handled error.\n */\n forEach(next: (value: T) => void): Promise;\n\n /**\n * @param next a handler for each value emitted by the observable\n * @param promiseCtor a constructor function used to instantiate the Promise\n * @return a promise that either resolves on observable completion or\n * rejects with the handled error\n * @deprecated Passing a Promise constructor will no longer be available\n * in upcoming versions of RxJS. This is because it adds weight to the library, for very\n * little benefit. If you need this functionality, it is recommended that you either\n * polyfill Promise, or you create an adapter to convert the returned native promise\n * to whatever promise implementation you wanted. Will be removed in v8.\n */\n forEach(next: (value: T) => void, promiseCtor: PromiseConstructorLike): Promise;\n\n forEach(next: (value: T) => void, promiseCtor?: PromiseConstructorLike): Promise {\n promiseCtor = getPromiseCtor(promiseCtor);\n\n return new promiseCtor((resolve, reject) => {\n const subscriber = new SafeSubscriber({\n next: (value) => {\n try {\n next(value);\n } catch (err) {\n reject(err);\n subscriber.unsubscribe();\n }\n },\n error: reject,\n complete: resolve,\n });\n this.subscribe(subscriber);\n }) as Promise;\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): TeardownLogic {\n return this.source?.subscribe(subscriber);\n }\n\n /**\n * An interop point defined by the es7-observable spec https://github.com/zenparsing/es-observable\n * @return This instance of the observable.\n */\n [Symbol_observable]() {\n return this;\n }\n\n /* tslint:disable:max-line-length */\n pipe(): Observable;\n pipe(op1: OperatorFunction): Observable;\n pipe(op1: OperatorFunction, op2: OperatorFunction): Observable;\n pipe(op1: OperatorFunction, op2: OperatorFunction, op3: OperatorFunction): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction,\n op9: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction,\n op9: OperatorFunction,\n ...operations: OperatorFunction[]\n ): Observable;\n /* tslint:enable:max-line-length */\n\n /**\n * Used to stitch together functional operators into a chain.\n *\n * ## Example\n *\n * ```ts\n * import { interval, filter, map, scan } from 'rxjs';\n *\n * interval(1000)\n * .pipe(\n * filter(x => x % 2 === 0),\n * map(x => x + x),\n * scan((acc, x) => acc + x)\n * )\n * .subscribe(x => console.log(x));\n * ```\n *\n * @return The Observable result of all the operators having been called\n * in the order they were passed in.\n */\n pipe(...operations: OperatorFunction[]): Observable {\n return pipeFromArray(operations)(this);\n }\n\n /* tslint:disable:max-line-length */\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(): Promise;\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(PromiseCtor: typeof Promise): Promise;\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(PromiseCtor: PromiseConstructorLike): Promise;\n /* tslint:enable:max-line-length */\n\n /**\n * Subscribe to this Observable and get a Promise resolving on\n * `complete` with the last emission (if any).\n *\n * **WARNING**: Only use this with observables you *know* will complete. If the source\n * observable does not complete, you will end up with a promise that is hung up, and\n * potentially all of the state of an async function hanging out in memory. To avoid\n * this situation, look into adding something like {@link timeout}, {@link take},\n * {@link takeWhile}, or {@link takeUntil} amongst others.\n *\n * @param [promiseCtor] a constructor function used to instantiate\n * the Promise\n * @return A Promise that resolves with the last value emit, or\n * rejects on an error. If there were no emissions, Promise\n * resolves with undefined.\n * @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise\n */\n toPromise(promiseCtor?: PromiseConstructorLike): Promise {\n promiseCtor = getPromiseCtor(promiseCtor);\n\n return new promiseCtor((resolve, reject) => {\n let value: T | undefined;\n this.subscribe(\n (x: T) => (value = x),\n (err: any) => reject(err),\n () => resolve(value)\n );\n }) as Promise;\n }\n}\n\n/**\n * Decides between a passed promise constructor from consuming code,\n * A default configured promise constructor, and the native promise\n * constructor and returns it. If nothing can be found, it will throw\n * an error.\n * @param promiseCtor The optional promise constructor to passed by consuming code\n */\nfunction getPromiseCtor(promiseCtor: PromiseConstructorLike | undefined) {\n return promiseCtor ?? config.Promise ?? Promise;\n}\n\nfunction isObserver(value: any): value is Observer {\n return value && isFunction(value.next) && isFunction(value.error) && isFunction(value.complete);\n}\n\nfunction isSubscriber(value: any): value is Subscriber {\n return (value && value instanceof Subscriber) || (isObserver(value) && isSubscription(value));\n}\n", "import { Observable } from '../Observable';\nimport { Subscriber } from '../Subscriber';\nimport { OperatorFunction } from '../types';\nimport { isFunction } from './isFunction';\n\n/**\n * Used to determine if an object is an Observable with a lift function.\n */\nexport function hasLift(source: any): source is { lift: InstanceType['lift'] } {\n return isFunction(source?.lift);\n}\n\n/**\n * Creates an `OperatorFunction`. Used to define operators throughout the library in a concise way.\n * @param init The logic to connect the liftedSource to the subscriber at the moment of subscription.\n */\nexport function operate(\n init: (liftedSource: Observable, subscriber: Subscriber) => (() => void) | void\n): OperatorFunction {\n return (source: Observable) => {\n if (hasLift(source)) {\n return source.lift(function (this: Subscriber, liftedSource: Observable) {\n try {\n return init(liftedSource, this);\n } catch (err) {\n this.error(err);\n }\n });\n }\n throw new TypeError('Unable to lift unknown Observable type');\n };\n}\n", "import { Subscriber } from '../Subscriber';\n\n/**\n * Creates an instance of an `OperatorSubscriber`.\n * @param destination The downstream subscriber.\n * @param onNext Handles next values, only called if this subscriber is not stopped or closed. Any\n * error that occurs in this function is caught and sent to the `error` method of this subscriber.\n * @param onError Handles errors from the subscription, any errors that occur in this handler are caught\n * and send to the `destination` error handler.\n * @param onComplete Handles completion notification from the subscription. Any errors that occur in\n * this handler are sent to the `destination` error handler.\n * @param onFinalize Additional teardown logic here. This will only be called on teardown if the\n * subscriber itself is not already closed. This is called after all other teardown logic is executed.\n */\nexport function createOperatorSubscriber(\n destination: Subscriber,\n onNext?: (value: T) => void,\n onComplete?: () => void,\n onError?: (err: any) => void,\n onFinalize?: () => void\n): Subscriber {\n return new OperatorSubscriber(destination, onNext, onComplete, onError, onFinalize);\n}\n\n/**\n * A generic helper for allowing operators to be created with a Subscriber and\n * use closures to capture necessary state from the operator function itself.\n */\nexport class OperatorSubscriber extends Subscriber {\n /**\n * Creates an instance of an `OperatorSubscriber`.\n * @param destination The downstream subscriber.\n * @param onNext Handles next values, only called if this subscriber is not stopped or closed. Any\n * error that occurs in this function is caught and sent to the `error` method of this subscriber.\n * @param onError Handles errors from the subscription, any errors that occur in this handler are caught\n * and send to the `destination` error handler.\n * @param onComplete Handles completion notification from the subscription. Any errors that occur in\n * this handler are sent to the `destination` error handler.\n * @param onFinalize Additional finalization logic here. This will only be called on finalization if the\n * subscriber itself is not already closed. This is called after all other finalization logic is executed.\n * @param shouldUnsubscribe An optional check to see if an unsubscribe call should truly unsubscribe.\n * NOTE: This currently **ONLY** exists to support the strange behavior of {@link groupBy}, where unsubscription\n * to the resulting observable does not actually disconnect from the source if there are active subscriptions\n * to any grouped observable. (DO NOT EXPOSE OR USE EXTERNALLY!!!)\n */\n constructor(\n destination: Subscriber,\n onNext?: (value: T) => void,\n onComplete?: () => void,\n onError?: (err: any) => void,\n private onFinalize?: () => void,\n private shouldUnsubscribe?: () => boolean\n ) {\n // It's important - for performance reasons - that all of this class's\n // members are initialized and that they are always initialized in the same\n // order. This will ensure that all OperatorSubscriber instances have the\n // same hidden class in V8. This, in turn, will help keep the number of\n // hidden classes involved in property accesses within the base class as\n // low as possible. If the number of hidden classes involved exceeds four,\n // the property accesses will become megamorphic and performance penalties\n // will be incurred - i.e. inline caches won't be used.\n //\n // The reasons for ensuring all instances have the same hidden class are\n // further discussed in this blog post from Benedikt Meurer:\n // https://benediktmeurer.de/2018/03/23/impact-of-polymorphism-on-component-based-frameworks-like-react/\n super(destination);\n this._next = onNext\n ? function (this: OperatorSubscriber, value: T) {\n try {\n onNext(value);\n } catch (err) {\n destination.error(err);\n }\n }\n : super._next;\n this._error = onError\n ? function (this: OperatorSubscriber, err: any) {\n try {\n onError(err);\n } catch (err) {\n // Send any errors that occur down stream.\n destination.error(err);\n } finally {\n // Ensure finalization.\n this.unsubscribe();\n }\n }\n : super._error;\n this._complete = onComplete\n ? function (this: OperatorSubscriber) {\n try {\n onComplete();\n } catch (err) {\n // Send any errors that occur down stream.\n destination.error(err);\n } finally {\n // Ensure finalization.\n this.unsubscribe();\n }\n }\n : super._complete;\n }\n\n unsubscribe() {\n if (!this.shouldUnsubscribe || this.shouldUnsubscribe()) {\n const { closed } = this;\n super.unsubscribe();\n // Execute additional teardown if we have any and we didn't already do so.\n !closed && this.onFinalize?.();\n }\n }\n}\n", "import { Subscription } from '../Subscription';\n\ninterface AnimationFrameProvider {\n schedule(callback: FrameRequestCallback): Subscription;\n requestAnimationFrame: typeof requestAnimationFrame;\n cancelAnimationFrame: typeof cancelAnimationFrame;\n delegate:\n | {\n requestAnimationFrame: typeof requestAnimationFrame;\n cancelAnimationFrame: typeof cancelAnimationFrame;\n }\n | undefined;\n}\n\nexport const animationFrameProvider: AnimationFrameProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n schedule(callback) {\n let request = requestAnimationFrame;\n let cancel: typeof cancelAnimationFrame | undefined = cancelAnimationFrame;\n const { delegate } = animationFrameProvider;\n if (delegate) {\n request = delegate.requestAnimationFrame;\n cancel = delegate.cancelAnimationFrame;\n }\n const handle = request((timestamp) => {\n // Clear the cancel function. The request has been fulfilled, so\n // attempting to cancel the request upon unsubscription would be\n // pointless.\n cancel = undefined;\n callback(timestamp);\n });\n return new Subscription(() => cancel?.(handle));\n },\n requestAnimationFrame(...args) {\n const { delegate } = animationFrameProvider;\n return (delegate?.requestAnimationFrame || requestAnimationFrame)(...args);\n },\n cancelAnimationFrame(...args) {\n const { delegate } = animationFrameProvider;\n return (delegate?.cancelAnimationFrame || cancelAnimationFrame)(...args);\n },\n delegate: undefined,\n};\n", "import { createErrorClass } from './createErrorClass';\n\nexport interface ObjectUnsubscribedError extends Error {}\n\nexport interface ObjectUnsubscribedErrorCtor {\n /**\n * @deprecated Internal implementation detail. Do not construct error instances.\n * Cannot be tagged as internal: https://github.com/ReactiveX/rxjs/issues/6269\n */\n new (): ObjectUnsubscribedError;\n}\n\n/**\n * An error thrown when an action is invalid because the object has been\n * unsubscribed.\n *\n * @see {@link Subject}\n * @see {@link BehaviorSubject}\n *\n * @class ObjectUnsubscribedError\n */\nexport const ObjectUnsubscribedError: ObjectUnsubscribedErrorCtor = createErrorClass(\n (_super) =>\n function ObjectUnsubscribedErrorImpl(this: any) {\n _super(this);\n this.name = 'ObjectUnsubscribedError';\n this.message = 'object unsubscribed';\n }\n);\n", "import { Operator } from './Operator';\nimport { Observable } from './Observable';\nimport { Subscriber } from './Subscriber';\nimport { Subscription, EMPTY_SUBSCRIPTION } from './Subscription';\nimport { Observer, SubscriptionLike, TeardownLogic } from './types';\nimport { ObjectUnsubscribedError } from './util/ObjectUnsubscribedError';\nimport { arrRemove } from './util/arrRemove';\nimport { errorContext } from './util/errorContext';\n\n/**\n * A Subject is a special type of Observable that allows values to be\n * multicasted to many Observers. Subjects are like EventEmitters.\n *\n * Every Subject is an Observable and an Observer. You can subscribe to a\n * Subject, and you can call next to feed values as well as error and complete.\n */\nexport class Subject extends Observable implements SubscriptionLike {\n closed = false;\n\n private currentObservers: Observer[] | null = null;\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n observers: Observer[] = [];\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n isStopped = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n hasError = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n thrownError: any = null;\n\n /**\n * Creates a \"subject\" by basically gluing an observer to an observable.\n *\n * @deprecated Recommended you do not use. Will be removed at some point in the future. Plans for replacement still under discussion.\n */\n static create: (...args: any[]) => any = (destination: Observer, source: Observable): AnonymousSubject => {\n return new AnonymousSubject(destination, source);\n };\n\n constructor() {\n // NOTE: This must be here to obscure Observable's constructor.\n super();\n }\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n lift(operator: Operator): Observable {\n const subject = new AnonymousSubject(this, this);\n subject.operator = operator as any;\n return subject as any;\n }\n\n /** @internal */\n protected _throwIfClosed() {\n if (this.closed) {\n throw new ObjectUnsubscribedError();\n }\n }\n\n next(value: T) {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n if (!this.currentObservers) {\n this.currentObservers = Array.from(this.observers);\n }\n for (const observer of this.currentObservers) {\n observer.next(value);\n }\n }\n });\n }\n\n error(err: any) {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n this.hasError = this.isStopped = true;\n this.thrownError = err;\n const { observers } = this;\n while (observers.length) {\n observers.shift()!.error(err);\n }\n }\n });\n }\n\n complete() {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n this.isStopped = true;\n const { observers } = this;\n while (observers.length) {\n observers.shift()!.complete();\n }\n }\n });\n }\n\n unsubscribe() {\n this.isStopped = this.closed = true;\n this.observers = this.currentObservers = null!;\n }\n\n get observed() {\n return this.observers?.length > 0;\n }\n\n /** @internal */\n protected _trySubscribe(subscriber: Subscriber): TeardownLogic {\n this._throwIfClosed();\n return super._trySubscribe(subscriber);\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n this._throwIfClosed();\n this._checkFinalizedStatuses(subscriber);\n return this._innerSubscribe(subscriber);\n }\n\n /** @internal */\n protected _innerSubscribe(subscriber: Subscriber) {\n const { hasError, isStopped, observers } = this;\n if (hasError || isStopped) {\n return EMPTY_SUBSCRIPTION;\n }\n this.currentObservers = null;\n observers.push(subscriber);\n return new Subscription(() => {\n this.currentObservers = null;\n arrRemove(observers, subscriber);\n });\n }\n\n /** @internal */\n protected _checkFinalizedStatuses(subscriber: Subscriber) {\n const { hasError, thrownError, isStopped } = this;\n if (hasError) {\n subscriber.error(thrownError);\n } else if (isStopped) {\n subscriber.complete();\n }\n }\n\n /**\n * Creates a new Observable with this Subject as the source. You can do this\n * to create custom Observer-side logic of the Subject and conceal it from\n * code that uses the Observable.\n * @return Observable that this Subject casts to.\n */\n asObservable(): Observable {\n const observable: any = new Observable();\n observable.source = this;\n return observable;\n }\n}\n\nexport class AnonymousSubject extends Subject {\n constructor(\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n public destination?: Observer,\n source?: Observable\n ) {\n super();\n this.source = source;\n }\n\n next(value: T) {\n this.destination?.next?.(value);\n }\n\n error(err: any) {\n this.destination?.error?.(err);\n }\n\n complete() {\n this.destination?.complete?.();\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n return this.source?.subscribe(subscriber) ?? EMPTY_SUBSCRIPTION;\n }\n}\n", "import { Subject } from './Subject';\nimport { Subscriber } from './Subscriber';\nimport { Subscription } from './Subscription';\n\n/**\n * A variant of Subject that requires an initial value and emits its current\n * value whenever it is subscribed to.\n */\nexport class BehaviorSubject extends Subject {\n constructor(private _value: T) {\n super();\n }\n\n get value(): T {\n return this.getValue();\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n const subscription = super._subscribe(subscriber);\n !subscription.closed && subscriber.next(this._value);\n return subscription;\n }\n\n getValue(): T {\n const { hasError, thrownError, _value } = this;\n if (hasError) {\n throw thrownError;\n }\n this._throwIfClosed();\n return _value;\n }\n\n next(value: T): void {\n super.next((this._value = value));\n }\n}\n", "import { TimestampProvider } from '../types';\n\ninterface DateTimestampProvider extends TimestampProvider {\n delegate: TimestampProvider | undefined;\n}\n\nexport const dateTimestampProvider: DateTimestampProvider = {\n now() {\n // Use the variable rather than `this` so that the function can be called\n // without being bound to the provider.\n return (dateTimestampProvider.delegate || Date).now();\n },\n delegate: undefined,\n};\n", "import { Subject } from './Subject';\nimport { TimestampProvider } from './types';\nimport { Subscriber } from './Subscriber';\nimport { Subscription } from './Subscription';\nimport { dateTimestampProvider } from './scheduler/dateTimestampProvider';\n\n/**\n * A variant of {@link Subject} that \"replays\" old values to new subscribers by emitting them when they first subscribe.\n *\n * `ReplaySubject` has an internal buffer that will store a specified number of values that it has observed. Like `Subject`,\n * `ReplaySubject` \"observes\" values by having them passed to its `next` method. When it observes a value, it will store that\n * value for a time determined by the configuration of the `ReplaySubject`, as passed to its constructor.\n *\n * When a new subscriber subscribes to the `ReplaySubject` instance, it will synchronously emit all values in its buffer in\n * a First-In-First-Out (FIFO) manner. The `ReplaySubject` will also complete, if it has observed completion; and it will\n * error if it has observed an error.\n *\n * There are two main configuration items to be concerned with:\n *\n * 1. `bufferSize` - This will determine how many items are stored in the buffer, defaults to infinite.\n * 2. `windowTime` - The amount of time to hold a value in the buffer before removing it from the buffer.\n *\n * Both configurations may exist simultaneously. So if you would like to buffer a maximum of 3 values, as long as the values\n * are less than 2 seconds old, you could do so with a `new ReplaySubject(3, 2000)`.\n *\n * ### Differences with BehaviorSubject\n *\n * `BehaviorSubject` is similar to `new ReplaySubject(1)`, with a couple of exceptions:\n *\n * 1. `BehaviorSubject` comes \"primed\" with a single value upon construction.\n * 2. `ReplaySubject` will replay values, even after observing an error, where `BehaviorSubject` will not.\n *\n * @see {@link Subject}\n * @see {@link BehaviorSubject}\n * @see {@link shareReplay}\n */\nexport class ReplaySubject extends Subject {\n private _buffer: (T | number)[] = [];\n private _infiniteTimeWindow = true;\n\n /**\n * @param _bufferSize The size of the buffer to replay on subscription\n * @param _windowTime The amount of time the buffered items will stay buffered\n * @param _timestampProvider An object with a `now()` method that provides the current timestamp. This is used to\n * calculate the amount of time something has been buffered.\n */\n constructor(\n private _bufferSize = Infinity,\n private _windowTime = Infinity,\n private _timestampProvider: TimestampProvider = dateTimestampProvider\n ) {\n super();\n this._infiniteTimeWindow = _windowTime === Infinity;\n this._bufferSize = Math.max(1, _bufferSize);\n this._windowTime = Math.max(1, _windowTime);\n }\n\n next(value: T): void {\n const { isStopped, _buffer, _infiniteTimeWindow, _timestampProvider, _windowTime } = this;\n if (!isStopped) {\n _buffer.push(value);\n !_infiniteTimeWindow && _buffer.push(_timestampProvider.now() + _windowTime);\n }\n this._trimBuffer();\n super.next(value);\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n this._throwIfClosed();\n this._trimBuffer();\n\n const subscription = this._innerSubscribe(subscriber);\n\n const { _infiniteTimeWindow, _buffer } = this;\n // We use a copy here, so reentrant code does not mutate our array while we're\n // emitting it to a new subscriber.\n const copy = _buffer.slice();\n for (let i = 0; i < copy.length && !subscriber.closed; i += _infiniteTimeWindow ? 1 : 2) {\n subscriber.next(copy[i] as T);\n }\n\n this._checkFinalizedStatuses(subscriber);\n\n return subscription;\n }\n\n private _trimBuffer() {\n const { _bufferSize, _timestampProvider, _buffer, _infiniteTimeWindow } = this;\n // If we don't have an infinite buffer size, and we're over the length,\n // use splice to truncate the old buffer values off. Note that we have to\n // double the size for instances where we're not using an infinite time window\n // because we're storing the values and the timestamps in the same array.\n const adjustedBufferSize = (_infiniteTimeWindow ? 1 : 2) * _bufferSize;\n _bufferSize < Infinity && adjustedBufferSize < _buffer.length && _buffer.splice(0, _buffer.length - adjustedBufferSize);\n\n // Now, if we're not in an infinite time window, remove all values where the time is\n // older than what is allowed.\n if (!_infiniteTimeWindow) {\n const now = _timestampProvider.now();\n let last = 0;\n // Search the array for the first timestamp that isn't expired and\n // truncate the buffer up to that point.\n for (let i = 1; i < _buffer.length && (_buffer[i] as number) <= now; i += 2) {\n last = i;\n }\n last && _buffer.splice(0, last + 1);\n }\n }\n}\n", "import { Scheduler } from '../Scheduler';\nimport { Subscription } from '../Subscription';\nimport { SchedulerAction } from '../types';\n\n/**\n * A unit of work to be executed in a `scheduler`. An action is typically\n * created from within a {@link SchedulerLike} and an RxJS user does not need to concern\n * themselves about creating and manipulating an Action.\n *\n * ```ts\n * class Action extends Subscription {\n * new (scheduler: Scheduler, work: (state?: T) => void);\n * schedule(state?: T, delay: number = 0): Subscription;\n * }\n * ```\n */\nexport class Action extends Subscription {\n constructor(scheduler: Scheduler, work: (this: SchedulerAction, state?: T) => void) {\n super();\n }\n /**\n * Schedules this action on its parent {@link SchedulerLike} for execution. May be passed\n * some context object, `state`. May happen at some point in the future,\n * according to the `delay` parameter, if specified.\n * @param state Some contextual data that the `work` function uses when called by the\n * Scheduler.\n * @param delay Time to wait before executing the work, where the time unit is implicit\n * and defined by the Scheduler.\n * @return A subscription in order to be able to unsubscribe the scheduled work.\n */\n public schedule(state?: T, delay: number = 0): Subscription {\n return this;\n }\n}\n", "import type { TimerHandle } from './timerHandle';\ntype SetIntervalFunction = (handler: () => void, timeout?: number, ...args: any[]) => TimerHandle;\ntype ClearIntervalFunction = (handle: TimerHandle) => void;\n\ninterface IntervalProvider {\n setInterval: SetIntervalFunction;\n clearInterval: ClearIntervalFunction;\n delegate:\n | {\n setInterval: SetIntervalFunction;\n clearInterval: ClearIntervalFunction;\n }\n | undefined;\n}\n\nexport const intervalProvider: IntervalProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n setInterval(handler: () => void, timeout?: number, ...args) {\n const { delegate } = intervalProvider;\n if (delegate?.setInterval) {\n return delegate.setInterval(handler, timeout, ...args);\n }\n return setInterval(handler, timeout, ...args);\n },\n clearInterval(handle) {\n const { delegate } = intervalProvider;\n return (delegate?.clearInterval || clearInterval)(handle as any);\n },\n delegate: undefined,\n};\n", "import { Action } from './Action';\nimport { SchedulerAction } from '../types';\nimport { Subscription } from '../Subscription';\nimport { AsyncScheduler } from './AsyncScheduler';\nimport { intervalProvider } from './intervalProvider';\nimport { arrRemove } from '../util/arrRemove';\nimport { TimerHandle } from './timerHandle';\n\nexport class AsyncAction extends Action {\n public id: TimerHandle | undefined;\n public state?: T;\n // @ts-ignore: Property has no initializer and is not definitely assigned\n public delay: number;\n protected pending: boolean = false;\n\n constructor(protected scheduler: AsyncScheduler, protected work: (this: SchedulerAction, state?: T) => void) {\n super(scheduler, work);\n }\n\n public schedule(state?: T, delay: number = 0): Subscription {\n if (this.closed) {\n return this;\n }\n\n // Always replace the current state with the new state.\n this.state = state;\n\n const id = this.id;\n const scheduler = this.scheduler;\n\n //\n // Important implementation note:\n //\n // Actions only execute once by default, unless rescheduled from within the\n // scheduled callback. This allows us to implement single and repeat\n // actions via the same code path, without adding API surface area, as well\n // as mimic traditional recursion but across asynchronous boundaries.\n //\n // However, JS runtimes and timers distinguish between intervals achieved by\n // serial `setTimeout` calls vs. a single `setInterval` call. An interval of\n // serial `setTimeout` calls can be individually delayed, which delays\n // scheduling the next `setTimeout`, and so on. `setInterval` attempts to\n // guarantee the interval callback will be invoked more precisely to the\n // interval period, regardless of load.\n //\n // Therefore, we use `setInterval` to schedule single and repeat actions.\n // If the action reschedules itself with the same delay, the interval is not\n // canceled. If the action doesn't reschedule, or reschedules with a\n // different delay, the interval will be canceled after scheduled callback\n // execution.\n //\n if (id != null) {\n this.id = this.recycleAsyncId(scheduler, id, delay);\n }\n\n // Set the pending flag indicating that this action has been scheduled, or\n // has recursively rescheduled itself.\n this.pending = true;\n\n this.delay = delay;\n // If this action has already an async Id, don't request a new one.\n this.id = this.id ?? this.requestAsyncId(scheduler, this.id, delay);\n\n return this;\n }\n\n protected requestAsyncId(scheduler: AsyncScheduler, _id?: TimerHandle, delay: number = 0): TimerHandle {\n return intervalProvider.setInterval(scheduler.flush.bind(scheduler, this), delay);\n }\n\n protected recycleAsyncId(_scheduler: AsyncScheduler, id?: TimerHandle, delay: number | null = 0): TimerHandle | undefined {\n // If this action is rescheduled with the same delay time, don't clear the interval id.\n if (delay != null && this.delay === delay && this.pending === false) {\n return id;\n }\n // Otherwise, if the action's delay time is different from the current delay,\n // or the action has been rescheduled before it's executed, clear the interval id\n if (id != null) {\n intervalProvider.clearInterval(id);\n }\n\n return undefined;\n }\n\n /**\n * Immediately executes this action and the `work` it contains.\n */\n public execute(state: T, delay: number): any {\n if (this.closed) {\n return new Error('executing a cancelled action');\n }\n\n this.pending = false;\n const error = this._execute(state, delay);\n if (error) {\n return error;\n } else if (this.pending === false && this.id != null) {\n // Dequeue if the action didn't reschedule itself. Don't call\n // unsubscribe(), because the action could reschedule later.\n // For example:\n // ```\n // scheduler.schedule(function doWork(counter) {\n // /* ... I'm a busy worker bee ... */\n // var originalAction = this;\n // /* wait 100ms before rescheduling the action */\n // setTimeout(function () {\n // originalAction.schedule(counter + 1);\n // }, 100);\n // }, 1000);\n // ```\n this.id = this.recycleAsyncId(this.scheduler, this.id, null);\n }\n }\n\n protected _execute(state: T, _delay: number): any {\n let errored: boolean = false;\n let errorValue: any;\n try {\n this.work(state);\n } catch (e) {\n errored = true;\n // HACK: Since code elsewhere is relying on the \"truthiness\" of the\n // return here, we can't have it return \"\" or 0 or false.\n // TODO: Clean this up when we refactor schedulers mid-version-8 or so.\n errorValue = e ? e : new Error('Scheduled action threw falsy error');\n }\n if (errored) {\n this.unsubscribe();\n return errorValue;\n }\n }\n\n unsubscribe() {\n if (!this.closed) {\n const { id, scheduler } = this;\n const { actions } = scheduler;\n\n this.work = this.state = this.scheduler = null!;\n this.pending = false;\n\n arrRemove(actions, this);\n if (id != null) {\n this.id = this.recycleAsyncId(scheduler, id, null);\n }\n\n this.delay = null!;\n super.unsubscribe();\n }\n }\n}\n", "import { Action } from './scheduler/Action';\nimport { Subscription } from './Subscription';\nimport { SchedulerLike, SchedulerAction } from './types';\nimport { dateTimestampProvider } from './scheduler/dateTimestampProvider';\n\n/**\n * An execution context and a data structure to order tasks and schedule their\n * execution. Provides a notion of (potentially virtual) time, through the\n * `now()` getter method.\n *\n * Each unit of work in a Scheduler is called an `Action`.\n *\n * ```ts\n * class Scheduler {\n * now(): number;\n * schedule(work, delay?, state?): Subscription;\n * }\n * ```\n *\n * @deprecated Scheduler is an internal implementation detail of RxJS, and\n * should not be used directly. Rather, create your own class and implement\n * {@link SchedulerLike}. Will be made internal in v8.\n */\nexport class Scheduler implements SchedulerLike {\n public static now: () => number = dateTimestampProvider.now;\n\n constructor(private schedulerActionCtor: typeof Action, now: () => number = Scheduler.now) {\n this.now = now;\n }\n\n /**\n * A getter method that returns a number representing the current time\n * (at the time this function was called) according to the scheduler's own\n * internal clock.\n * @return A number that represents the current time. May or may not\n * have a relation to wall-clock time. May or may not refer to a time unit\n * (e.g. milliseconds).\n */\n public now: () => number;\n\n /**\n * Schedules a function, `work`, for execution. May happen at some point in\n * the future, according to the `delay` parameter, if specified. May be passed\n * some context object, `state`, which will be passed to the `work` function.\n *\n * The given arguments will be processed an stored as an Action object in a\n * queue of actions.\n *\n * @param work A function representing a task, or some unit of work to be\n * executed by the Scheduler.\n * @param delay Time to wait before executing the work, where the time unit is\n * implicit and defined by the Scheduler itself.\n * @param state Some contextual data that the `work` function uses when called\n * by the Scheduler.\n * @return A subscription in order to be able to unsubscribe the scheduled work.\n */\n public schedule(work: (this: SchedulerAction, state?: T) => void, delay: number = 0, state?: T): Subscription {\n return new this.schedulerActionCtor(this, work).schedule(state, delay);\n }\n}\n", "import { Scheduler } from '../Scheduler';\nimport { Action } from './Action';\nimport { AsyncAction } from './AsyncAction';\nimport { TimerHandle } from './timerHandle';\n\nexport class AsyncScheduler extends Scheduler {\n public actions: Array> = [];\n /**\n * A flag to indicate whether the Scheduler is currently executing a batch of\n * queued actions.\n * @internal\n */\n public _active: boolean = false;\n /**\n * An internal ID used to track the latest asynchronous task such as those\n * coming from `setTimeout`, `setInterval`, `requestAnimationFrame`, and\n * others.\n * @internal\n */\n public _scheduled: TimerHandle | undefined;\n\n constructor(SchedulerAction: typeof Action, now: () => number = Scheduler.now) {\n super(SchedulerAction, now);\n }\n\n public flush(action: AsyncAction): void {\n const { actions } = this;\n\n if (this._active) {\n actions.push(action);\n return;\n }\n\n let error: any;\n this._active = true;\n\n do {\n if ((error = action.execute(action.state, action.delay))) {\n break;\n }\n } while ((action = actions.shift()!)); // exhaust the scheduler queue\n\n this._active = false;\n\n if (error) {\n while ((action = actions.shift()!)) {\n action.unsubscribe();\n }\n throw error;\n }\n }\n}\n", "import { AsyncAction } from './AsyncAction';\nimport { AsyncScheduler } from './AsyncScheduler';\n\n/**\n *\n * Async Scheduler\n *\n * Schedule task as if you used setTimeout(task, duration)\n *\n * `async` scheduler schedules tasks asynchronously, by putting them on the JavaScript\n * event loop queue. It is best used to delay tasks in time or to schedule tasks repeating\n * in intervals.\n *\n * If you just want to \"defer\" task, that is to perform it right after currently\n * executing synchronous code ends (commonly achieved by `setTimeout(deferredTask, 0)`),\n * better choice will be the {@link asapScheduler} scheduler.\n *\n * ## Examples\n * Use async scheduler to delay task\n * ```ts\n * import { asyncScheduler } from 'rxjs';\n *\n * const task = () => console.log('it works!');\n *\n * asyncScheduler.schedule(task, 2000);\n *\n * // After 2 seconds logs:\n * // \"it works!\"\n * ```\n *\n * Use async scheduler to repeat task in intervals\n * ```ts\n * import { asyncScheduler } from 'rxjs';\n *\n * function task(state) {\n * console.log(state);\n * this.schedule(state + 1, 1000); // `this` references currently executing Action,\n * // which we reschedule with new state and delay\n * }\n *\n * asyncScheduler.schedule(task, 3000, 0);\n *\n * // Logs:\n * // 0 after 3s\n * // 1 after 4s\n * // 2 after 5s\n * // 3 after 6s\n * ```\n */\n\nexport const asyncScheduler = new AsyncScheduler(AsyncAction);\n\n/**\n * @deprecated Renamed to {@link asyncScheduler}. Will be removed in v8.\n */\nexport const async = asyncScheduler;\n", "import { AsyncAction } from './AsyncAction';\nimport { Subscription } from '../Subscription';\nimport { QueueScheduler } from './QueueScheduler';\nimport { SchedulerAction } from '../types';\nimport { TimerHandle } from './timerHandle';\n\nexport class QueueAction extends AsyncAction {\n constructor(protected scheduler: QueueScheduler, protected work: (this: SchedulerAction, state?: T) => void) {\n super(scheduler, work);\n }\n\n public schedule(state?: T, delay: number = 0): Subscription {\n if (delay > 0) {\n return super.schedule(state, delay);\n }\n this.delay = delay;\n this.state = state;\n this.scheduler.flush(this);\n return this;\n }\n\n public execute(state: T, delay: number): any {\n return delay > 0 || this.closed ? super.execute(state, delay) : this._execute(state, delay);\n }\n\n protected requestAsyncId(scheduler: QueueScheduler, id?: TimerHandle, delay: number = 0): TimerHandle {\n // If delay exists and is greater than 0, or if the delay is null (the\n // action wasn't rescheduled) but was originally scheduled as an async\n // action, then recycle as an async action.\n\n if ((delay != null && delay > 0) || (delay == null && this.delay > 0)) {\n return super.requestAsyncId(scheduler, id, delay);\n }\n\n // Otherwise flush the scheduler starting with this action.\n scheduler.flush(this);\n\n // HACK: In the past, this was returning `void`. However, `void` isn't a valid\n // `TimerHandle`, and generally the return value here isn't really used. So the\n // compromise is to return `0` which is both \"falsy\" and a valid `TimerHandle`,\n // as opposed to refactoring every other instanceo of `requestAsyncId`.\n return 0;\n }\n}\n", "import { AsyncScheduler } from './AsyncScheduler';\n\nexport class QueueScheduler extends AsyncScheduler {\n}\n", "import { QueueAction } from './QueueAction';\nimport { QueueScheduler } from './QueueScheduler';\n\n/**\n *\n * Queue Scheduler\n *\n * Put every next task on a queue, instead of executing it immediately\n *\n * `queue` scheduler, when used with delay, behaves the same as {@link asyncScheduler} scheduler.\n *\n * When used without delay, it schedules given task synchronously - executes it right when\n * it is scheduled. However when called recursively, that is when inside the scheduled task,\n * another task is scheduled with queue scheduler, instead of executing immediately as well,\n * that task will be put on a queue and wait for current one to finish.\n *\n * This means that when you execute task with `queue` scheduler, you are sure it will end\n * before any other task scheduled with that scheduler will start.\n *\n * ## Examples\n * Schedule recursively first, then do something\n * ```ts\n * import { queueScheduler } from 'rxjs';\n *\n * queueScheduler.schedule(() => {\n * queueScheduler.schedule(() => console.log('second')); // will not happen now, but will be put on a queue\n *\n * console.log('first');\n * });\n *\n * // Logs:\n * // \"first\"\n * // \"second\"\n * ```\n *\n * Reschedule itself recursively\n * ```ts\n * import { queueScheduler } from 'rxjs';\n *\n * queueScheduler.schedule(function(state) {\n * if (state !== 0) {\n * console.log('before', state);\n * this.schedule(state - 1); // `this` references currently executing Action,\n * // which we reschedule with new state\n * console.log('after', state);\n * }\n * }, 0, 3);\n *\n * // In scheduler that runs recursively, you would expect:\n * // \"before\", 3\n * // \"before\", 2\n * // \"before\", 1\n * // \"after\", 1\n * // \"after\", 2\n * // \"after\", 3\n *\n * // But with queue it logs:\n * // \"before\", 3\n * // \"after\", 3\n * // \"before\", 2\n * // \"after\", 2\n * // \"before\", 1\n * // \"after\", 1\n * ```\n */\n\nexport const queueScheduler = new QueueScheduler(QueueAction);\n\n/**\n * @deprecated Renamed to {@link queueScheduler}. Will be removed in v8.\n */\nexport const queue = queueScheduler;\n", "import { AsyncAction } from './AsyncAction';\nimport { AnimationFrameScheduler } from './AnimationFrameScheduler';\nimport { SchedulerAction } from '../types';\nimport { animationFrameProvider } from './animationFrameProvider';\nimport { TimerHandle } from './timerHandle';\n\nexport class AnimationFrameAction extends AsyncAction {\n constructor(protected scheduler: AnimationFrameScheduler, protected work: (this: SchedulerAction, state?: T) => void) {\n super(scheduler, work);\n }\n\n protected requestAsyncId(scheduler: AnimationFrameScheduler, id?: TimerHandle, delay: number = 0): TimerHandle {\n // If delay is greater than 0, request as an async action.\n if (delay !== null && delay > 0) {\n return super.requestAsyncId(scheduler, id, delay);\n }\n // Push the action to the end of the scheduler queue.\n scheduler.actions.push(this);\n // If an animation frame has already been requested, don't request another\n // one. If an animation frame hasn't been requested yet, request one. Return\n // the current animation frame request id.\n return scheduler._scheduled || (scheduler._scheduled = animationFrameProvider.requestAnimationFrame(() => scheduler.flush(undefined)));\n }\n\n protected recycleAsyncId(scheduler: AnimationFrameScheduler, id?: TimerHandle, delay: number = 0): TimerHandle | undefined {\n // If delay exists and is greater than 0, or if the delay is null (the\n // action wasn't rescheduled) but was originally scheduled as an async\n // action, then recycle as an async action.\n if (delay != null ? delay > 0 : this.delay > 0) {\n return super.recycleAsyncId(scheduler, id, delay);\n }\n // If the scheduler queue has no remaining actions with the same async id,\n // cancel the requested animation frame and set the scheduled flag to\n // undefined so the next AnimationFrameAction will request its own.\n const { actions } = scheduler;\n if (id != null && id === scheduler._scheduled && actions[actions.length - 1]?.id !== id) {\n animationFrameProvider.cancelAnimationFrame(id as number);\n scheduler._scheduled = undefined;\n }\n // Return undefined so the action knows to request a new async id if it's rescheduled.\n return undefined;\n }\n}\n", "import { AsyncAction } from './AsyncAction';\nimport { AsyncScheduler } from './AsyncScheduler';\n\nexport class AnimationFrameScheduler extends AsyncScheduler {\n public flush(action?: AsyncAction): void {\n this._active = true;\n // The async id that effects a call to flush is stored in _scheduled.\n // Before executing an action, it's necessary to check the action's async\n // id to determine whether it's supposed to be executed in the current\n // flush.\n // Previous implementations of this method used a count to determine this,\n // but that was unsound, as actions that are unsubscribed - i.e. cancelled -\n // are removed from the actions array and that can shift actions that are\n // scheduled to be executed in a subsequent flush into positions at which\n // they are executed within the current flush.\n let flushId;\n if (action) {\n flushId = action.id;\n } else {\n flushId = this._scheduled;\n this._scheduled = undefined;\n }\n\n const { actions } = this;\n let error: any;\n action = action || actions.shift()!;\n\n do {\n if ((error = action.execute(action.state, action.delay))) {\n break;\n }\n } while ((action = actions[0]) && action.id === flushId && actions.shift());\n\n this._active = false;\n\n if (error) {\n while ((action = actions[0]) && action.id === flushId && actions.shift()) {\n action.unsubscribe();\n }\n throw error;\n }\n }\n}\n", "import { AnimationFrameAction } from './AnimationFrameAction';\nimport { AnimationFrameScheduler } from './AnimationFrameScheduler';\n\n/**\n *\n * Animation Frame Scheduler\n *\n * Perform task when `window.requestAnimationFrame` would fire\n *\n * When `animationFrame` scheduler is used with delay, it will fall back to {@link asyncScheduler} scheduler\n * behaviour.\n *\n * Without delay, `animationFrame` scheduler can be used to create smooth browser animations.\n * It makes sure scheduled task will happen just before next browser content repaint,\n * thus performing animations as efficiently as possible.\n *\n * ## Example\n * Schedule div height animation\n * ```ts\n * // html:
\n * import { animationFrameScheduler } from 'rxjs';\n *\n * const div = document.querySelector('div');\n *\n * animationFrameScheduler.schedule(function(height) {\n * div.style.height = height + \"px\";\n *\n * this.schedule(height + 1); // `this` references currently executing Action,\n * // which we reschedule with new state\n * }, 0, 0);\n *\n * // You will see a div element growing in height\n * ```\n */\n\nexport const animationFrameScheduler = new AnimationFrameScheduler(AnimationFrameAction);\n\n/**\n * @deprecated Renamed to {@link animationFrameScheduler}. Will be removed in v8.\n */\nexport const animationFrame = animationFrameScheduler;\n", "import { Observable } from '../Observable';\nimport { SchedulerLike } from '../types';\n\n/**\n * A simple Observable that emits no items to the Observer and immediately\n * emits a complete notification.\n *\n * Just emits 'complete', and nothing else.\n *\n * ![](empty.png)\n *\n * A simple Observable that only emits the complete notification. It can be used\n * for composing with other Observables, such as in a {@link mergeMap}.\n *\n * ## Examples\n *\n * Log complete notification\n *\n * ```ts\n * import { EMPTY } from 'rxjs';\n *\n * EMPTY.subscribe({\n * next: () => console.log('Next'),\n * complete: () => console.log('Complete!')\n * });\n *\n * // Outputs\n * // Complete!\n * ```\n *\n * Emit the number 7, then complete\n *\n * ```ts\n * import { EMPTY, startWith } from 'rxjs';\n *\n * const result = EMPTY.pipe(startWith(7));\n * result.subscribe(x => console.log(x));\n *\n * // Outputs\n * // 7\n * ```\n *\n * Map and flatten only odd numbers to the sequence `'a'`, `'b'`, `'c'`\n *\n * ```ts\n * import { interval, mergeMap, of, EMPTY } from 'rxjs';\n *\n * const interval$ = interval(1000);\n * const result = interval$.pipe(\n * mergeMap(x => x % 2 === 1 ? of('a', 'b', 'c') : EMPTY),\n * );\n * result.subscribe(x => console.log(x));\n *\n * // Results in the following to the console:\n * // x is equal to the count on the interval, e.g. (0, 1, 2, 3, ...)\n * // x will occur every 1000ms\n * // if x % 2 is equal to 1, print a, b, c (each on its own)\n * // if x % 2 is not equal to 1, nothing will be output\n * ```\n *\n * @see {@link Observable}\n * @see {@link NEVER}\n * @see {@link of}\n * @see {@link throwError}\n */\nexport const EMPTY = new Observable((subscriber) => subscriber.complete());\n\n/**\n * @param scheduler A {@link SchedulerLike} to use for scheduling\n * the emission of the complete notification.\n * @deprecated Replaced with the {@link EMPTY} constant or {@link scheduled} (e.g. `scheduled([], scheduler)`). Will be removed in v8.\n */\nexport function empty(scheduler?: SchedulerLike) {\n return scheduler ? emptyScheduled(scheduler) : EMPTY;\n}\n\nfunction emptyScheduled(scheduler: SchedulerLike) {\n return new Observable((subscriber) => scheduler.schedule(() => subscriber.complete()));\n}\n", "import { SchedulerLike } from '../types';\nimport { isFunction } from './isFunction';\n\nexport function isScheduler(value: any): value is SchedulerLike {\n return value && isFunction(value.schedule);\n}\n", "import { SchedulerLike } from '../types';\nimport { isFunction } from './isFunction';\nimport { isScheduler } from './isScheduler';\n\nfunction last(arr: T[]): T | undefined {\n return arr[arr.length - 1];\n}\n\nexport function popResultSelector(args: any[]): ((...args: unknown[]) => unknown) | undefined {\n return isFunction(last(args)) ? args.pop() : undefined;\n}\n\nexport function popScheduler(args: any[]): SchedulerLike | undefined {\n return isScheduler(last(args)) ? args.pop() : undefined;\n}\n\nexport function popNumber(args: any[], defaultValue: number): number {\n return typeof last(args) === 'number' ? args.pop()! : defaultValue;\n}\n", "export const isArrayLike = ((x: any): x is ArrayLike => x && typeof x.length === 'number' && typeof x !== 'function');", "import { isFunction } from \"./isFunction\";\n\n/**\n * Tests to see if the object is \"thennable\".\n * @param value the object to test\n */\nexport function isPromise(value: any): value is PromiseLike {\n return isFunction(value?.then);\n}\n", "import { InteropObservable } from '../types';\nimport { observable as Symbol_observable } from '../symbol/observable';\nimport { isFunction } from './isFunction';\n\n/** Identifies an input as being Observable (but not necessary an Rx Observable) */\nexport function isInteropObservable(input: any): input is InteropObservable {\n return isFunction(input[Symbol_observable]);\n}\n", "import { isFunction } from './isFunction';\n\nexport function isAsyncIterable(obj: any): obj is AsyncIterable {\n return Symbol.asyncIterator && isFunction(obj?.[Symbol.asyncIterator]);\n}\n", "/**\n * Creates the TypeError to throw if an invalid object is passed to `from` or `scheduled`.\n * @param input The object that was passed.\n */\nexport function createInvalidObservableTypeError(input: any) {\n // TODO: We should create error codes that can be looked up, so this can be less verbose.\n return new TypeError(\n `You provided ${\n input !== null && typeof input === 'object' ? 'an invalid object' : `'${input}'`\n } where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.`\n );\n}\n", "export function getSymbolIterator(): symbol {\n if (typeof Symbol !== 'function' || !Symbol.iterator) {\n return '@@iterator' as any;\n }\n\n return Symbol.iterator;\n}\n\nexport const iterator = getSymbolIterator();\n", "import { iterator as Symbol_iterator } from '../symbol/iterator';\nimport { isFunction } from './isFunction';\n\n/** Identifies an input as being an Iterable */\nexport function isIterable(input: any): input is Iterable {\n return isFunction(input?.[Symbol_iterator]);\n}\n", "import { ReadableStreamLike } from '../types';\nimport { isFunction } from './isFunction';\n\nexport async function* readableStreamLikeToAsyncGenerator(readableStream: ReadableStreamLike): AsyncGenerator {\n const reader = readableStream.getReader();\n try {\n while (true) {\n const { value, done } = await reader.read();\n if (done) {\n return;\n }\n yield value!;\n }\n } finally {\n reader.releaseLock();\n }\n}\n\nexport function isReadableStreamLike(obj: any): obj is ReadableStreamLike {\n // We don't want to use instanceof checks because they would return\n // false for instances from another Realm, like an + +
+

如果您已经初步了解了 QPython 的功能,欢迎开始体验编程的乐趣!试试 Hello World 教程 迈出您的第一步。

+ + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/zh/index.html b/zh/index.html new file mode 100644 index 0000000..96a1324 --- /dev/null +++ b/zh/index.html @@ -0,0 +1,2986 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + 概览 - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

QPython 项目

+

QPython 不仅是一个强大的 Android Python IDE,也是一个活跃的技术社区。

+

QPython 横幅

+

支持 AI 的 Android Python IDE

+

QPython 是您进行 Android Python 编程的入口。它集成了 Python 解释器、AI 模型引擎和移动开发工具链,让您能够从移动设备构建 Web 应用程序、执行科学计算和创建智能应用。

+

无论您是在学习编程、构建数据科学项目,还是开发 AI 驱动的应用程序,QPython 都提供了完整的移动编程解决方案,拥有全面的开发者资源和活跃的社区来支持您的旅程。

+
    +
  • 分支版本 – 了解不同的 QPython 版本(IDE、社区版、Plus),选择适合您需求的版本
  • +
  • 更新日志 – 随时了解最新功能、改进和发行说明
  • +
+
+

快速开始

+

如何快速入门?请按照以下步骤:

+ +

编程指南

+

QPython 不仅提供基础的 Python 接口支持,更重要的是,它还允许您通过 QSL4A 接口使用 Python 调用 Android API。

+ +
+

下载资源

+

最新版本 v4.0.0 更新说明:

+
    +
  • 外部存储访问:用户现在可以将 Python 脚本直接保存到外部存储设备,大大提升了文件管理的灵活性。
  • +
  • QSL4A 功能增强:改进了 QSL4A 的功能。
  • +
  • +

    社区与课程:优化了社区和课程模块,提供更清晰的信息和更便捷的导航,方便用户访问学习资源和获得支持。

    +
  • +
  • +

    Google Drive

    +
  • +
  • 微信网盘
  • +
+

社区与反馈

+

QPython+ 学习助手

+ +

关注我们

+ +
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/zh/qpypi-guide/index.html b/zh/qpypi-guide/index.html new file mode 100644 index 0000000..4474b87 --- /dev/null +++ b/zh/qpypi-guide/index.html @@ -0,0 +1,2953 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + QPYPI - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + +

QPYPI

+

您可以通过安装包来扩展 QPython 的功能。

+

包安装支持

+

纯 Python 包

+

QPython 支持使用纯 Python 开发的 Python 包。您可以直接通过 QPython 的 PIP 客户端或仪表盘上的 QPYPI 使用 pip install 安装这些包。

+

预编译包

+

如果某些包(或它们的依赖)是用 Rust/C/C++ 开发的,QPython 无法直接支持它们,因为 QPython 上没有编译器工具链支持。但是,QPython 团队已经预编译了一些常用包,并在 QPython 的 QPYPI/Extensions 中发布,供用户轻松安装。

+

安装预编译包

+

您可以通过以下方式安装预编译包:

+
    +
  1. 通过 QPython 应用:直接从 QPython 应用内的 QPYPI 或 Extensions 安装
  2. +
  3. 通过 PyPI:访问 https://pypi.org/user/qpythonx/ 查看可用的包
  4. +
  5. 通过 pip 命令
  6. +
  7. pip install xxx-qpython - 带 -qpython 后缀的包
  8. +
  9. pip install xxx-aipy - 带 -aipy 后缀的包(通常是 AI/ML 相关的包)
  10. +
+
+

注意:我们通常根据包的预期用途添加这些后缀之一。

+
+

请求新包

+

如果您需要当前不支持的包:

+
    +
  • qpython.org 项目 中提出问题
  • +
  • QPython 团队将考虑预编译并将其添加到仓库中
  • +
+

获取更多帮助和参与社区的方式,请参阅 社区与反馈 部分。

+
+

注意:由于不同的计算机架构,我们无法保证 QPYPI 包含 PyPI 上的所有包。

+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/zh/qpython-x/index.html b/zh/qpython-x/index.html new file mode 100644 index 0000000..e37d97e --- /dev/null +++ b/zh/qpython-x/index.html @@ -0,0 +1,2914 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 版本 - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + +

QPython 版本

+

QPython 是 Android 的 Python 引擎。它包含令人惊叹的功能,如 Python 解释器、运行环境、编辑器、QPYI 和集成的 SL4A。它让您可以轻松地在 Android 上使用 Python。而且它是免费的。

+

QPython 已经在全球拥有数百万用户,也是一个开源项目。

+

针对不同的使用场景,QPython 有多个版本:

+

QPython

+

标准版:针对 AI 性能和通用应用商店兼容性优化

+

主要版本,可在 Google Play 和其他应用商店下载。此版本专注于 AI 功能,使 AI 时代用户更容易学习和使用 Python。

+

主要特性: +- AI 驱动的编码辅助和学习工具 +- 离线 Python 3.12 解释器:运行 Python 程序无需互联网 +- 支持多种项目类型:控制台、SL4A、WebApp +- 方便的二维码读取器,用于将代码传输到手机 +- QPYPI 和自定义仓库,用于预编译 wheel 包 +-易于使用的语法高亮编辑器 +- 完善的文档和社区支持

+

权限: 仅需基本手机权限即可安装。

+

下载: 可在 Google Play 和主要应用商店下载。

+

QPython+

+

社区版:公开支持各种社区驱动的功能;可在部分应用商店下载。

+

社区开源版本(正在规划和准备中)。此版本专为想要参与 QPython 项目开发的贡献者而设计,支持针对不同制造商的定制。

+

主要特性: +- 社区驱动开发 +- 支持供应商定制 +- 更灵活的配置选项 +- 开放供贡献者加入开发

+

权限: 仅需基本手机权限即可安装。

+

下载: 将在 Google Play 和主要应用商店下载。

+

注意: 此版本目前正在规划准备中。请关注更新!

+

QPython Plus

+

QPython+ 完全访问版:授予调用所有 Android 接口的完整权限。下载链接仅通过官方云盘提供。

+

具有扩展权限的特殊版本,提供对设备的最大控制。由于其敏感的权限要求,此版本不会在应用商店上发布。

+

主要特性: +- 完整的 SL4A API 访问,包括敏感功能 +- SMS/通话控制 API +- 高级系统集成 +- 最大的设备控制能力

+

权限: 需要广泛的权限,包括: +- 蓝牙 +- 位置(GPS) +- 读取/发送短信 +- 打电话 +- 相机和麦克风 +- 系统设置 +- 以及其他敏感权限

+

下载: 不在应用商店上架。仅通过特殊渠道分发。

+

重要提示: QPython 不会在后台未经您知悉使用这些权限。如果使用 SL4A API 时出现异常,请检查系统设置中是否已启用相关权限。

+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/zh/qsl4a/connectivity/contacts/index.html b/zh/qsl4a/connectivity/contacts/index.html new file mode 100644 index 0000000..9b7a940 --- /dev/null +++ b/zh/qsl4a/connectivity/contacts/index.html @@ -0,0 +1,3236 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 联系人 - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

联系人 API

+

访问和管理设备联系人。

+

选择联系人

+

pickContact()

+

显示联系人列表以供选择。

+
pickContact()
+
+

返回: 包含联系人 URI 的 Intent

+

pickPhone()

+

显示电话号码列表以供选择。

+
pickPhone()
+
+

返回: 选中的电话号码字符串

+

联系人查询

+

contactsGet()

+

获取所有联系人。

+
contactsGet(attributes=None)
+
+

参数: +- attributes (list, optional): 要检索的特定属性

+

返回: 联系人 JSONObject 列表

+

contactsGetById()

+

通过 ID 获取联系人。

+
contactsGetById(id, attributes=None)
+
+

参数: +- id (int): 联系人 ID +- attributes (list, optional): 要检索的特定属性

+

返回: JSONObject 联系人数据

+

contactsGetCount()

+

获取联系人总数。

+
contactsGetCount()
+
+

返回: 整数计数

+

contactsGetIds()

+

获取所有联系人 ID。

+
contactsGetIds()
+
+

返回: 联系人 ID 整数列表

+

contactsGetAttributes()

+

获取所有可能的联系人属性。

+
contactsGetAttributes()
+
+

返回: 属性名称列表

+

内容查询

+

queryContent()

+

使用自定义参数查询内容解析器。

+
queryContent(uri, attributes=None, selection=None, selectionArgs=None, order=None)
+
+

参数: +- uri (str): 内容 URI +- attributes (list, optional): 要检索的属性 +- selection (str, optional): WHERE 子句 +- selectionArgs (list, optional): 选择参数 +- order (str, optional): ORDER BY 子句

+

返回: JSONObject 结果列表

+

queryAttributes()

+

获取内容 URI 的属性。

+
queryAttributes(uri)
+
+

参数: +- uri (str): 内容 URI

+

返回: 属性名称的 JSONArray

+

使用示例

+
import androidhelper
+
+droid = androidhelper.Android()
+
+# 选择联系人
+contact_uri = droid.pickContact().result
+print(f"Selected contact: {contact_uri}")
+
+# 选择电话号码
+phone = droid.pickPhone().result
+print(f"Selected phone: {phone}")
+
+# 获取所有联系人
+contacts = droid.contactsGet().result
+print(f"Total contacts: {len(contacts)}")
+
+# 通过 ID 获取联系人
+contact = droid.contactsGetById(1).result
+print(f"Contact: {contact}")
+
+# 获取联系人属性
+attrs = droid.contactsGetAttributes().result
+print(f"Available attributes: {attrs}")
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/zh/qsl4a/connectivity/ftp/index.html b/zh/qsl4a/connectivity/ftp/index.html new file mode 100644 index 0000000..dcb9214 --- /dev/null +++ b/zh/qsl4a/connectivity/ftp/index.html @@ -0,0 +1,3080 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + FTP 服务器 - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + +

FTP 服务器 API

+

在设备上启动和管理内置 FTP 服务器。

+

FTP 服务器方法

+

ftpStart()

+

启动 FTP 服务器。

+
ftpStart()
+
+

返回: 包含 IP 地址和端口的数组 [ip, port]

+

ftpStop()

+

停止 FTP 服务器。

+
ftpStop()
+
+

ftpIsRunning()

+

检查 FTP 服务器是否正在运行。

+
ftpIsRunning()
+
+

返回: 如果正在运行则为 True

+

ftpGet()

+

获取 FTP 服务器 IP 地址。

+
ftpGet()
+
+

返回: 包含 IP 地址和端口的数组

+

ftpSet()

+

配置 FTP 服务器设置。

+
ftpSet(port=None, rootDir=None, username=None, password=None)
+
+

参数: +- port (int, optional): 服务器端口 +- rootDir (str, optional): 要服务的基础目录 +- username (str, optional): 登录用户名 +- password (str, optional): 登录密码

+

返回: 包含当前设置的 JSONObject

+

ftpStatus()

+

获取 FTP 服务器状态。

+
ftpStatus()
+
+

返回: 状态描述字符串

+

使用示例

+
import androidhelper
+
+droid = androidhelper.Android()
+
+# 配置 FTP 服务器
+droid.ftpSet(
+    port=2121,
+    rootDir="/sdcard",
+    username="admin",
+    password="secret"
+)
+
+# 启动 FTP 服务器
+info = droid.ftpStart().result
+print(f"FTP running at {info[0]}:{info[1]}")
+
+# 检查状态
+if droid.ftpIsRunning().result:
+    print("FTP server is running")
+
+# 获取服务器信息
+server_info = droid.ftpGet().result
+print(f"Server: {server_info}")
+
+# 完成后停止
+droid.ftpStop()
+
+

注意: 使用提供的凭据通过任何 FTP 客户端连接到 FTP 服务器。

+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/zh/qsl4a/connectivity/location/index.html b/zh/qsl4a/connectivity/location/index.html new file mode 100644 index 0000000..e4b0b29 --- /dev/null +++ b/zh/qsl4a/connectivity/location/index.html @@ -0,0 +1,3160 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 位置 - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

位置 API

+

访问 GPS 和网络位置服务。

+

方法

+

startLocating()

+

开始位置更新。

+
startLocating(minUpdateTime=60000, minUpdateDistance=30, updateGnssStatus=False)
+
+

参数: +- minUpdateTime (int): 更新之间的最小时间(毫秒)(默认:60000) +- minUpdateDistance (float): 更新的最小距离(米)(默认:30) +- updateGnssStatus (bool): 启用 GNSS 状态更新(默认:False)

+

stopLocating()

+

停止位置更新。

+
stopLocating()
+
+

readLocation()

+

获取最后已知位置。

+
readLocation()
+
+

返回: 位置数据字典

+

getLastKnownLocation()

+

获取缓存的位置。

+
getLastKnownLocation()
+
+

返回: 来自所有提供商的位置

+

geocode()

+

将地址转换为坐标。

+
geocode(address, maxResults=1)
+
+

位置提供商方法)

+

locationProviders()

+

获取手机上可用的位置提供商。

+
locationProviders()
+
+

返回: 可用提供商名称列表(例如 ['gps', 'network'])

+

locationProviderEnabled()

+

检查特定位置提供商是否已启用。

+
locationProviderEnabled(provider)
+
+

参数: +- provider (str): 提供商名称(例如 'gps', 'network')

+

返回: 如果已启用则为 True,否则为 False

+

readGnssStatus()

+

读取全球导航卫星系统状态(需要 Android 8+)。

+
readGnssStatus()
+
+

返回: 包含 GNSS 卫星信息的 JSONArray

+

使用示例

+
import androidhelper
+import time
+
+droid = androidhelper.Android()
+
+# 开始位置更新
+droid.startLocating(minUpdateTime=5000, minUpdateDistance=1, updateGnssStatus=False)
+
+# 等待定位
+time.sleep(10)
+
+# 获取位置
+loc = droid.readLocation().result
+if loc:
+    lat = loc['latitude']
+    lon = loc['longitude']
+    print(f"Location: {lat}, {lon}")
+
+droid.stopLocating()
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/zh/qsl4a/connectivity/phone/index.html b/zh/qsl4a/connectivity/phone/index.html new file mode 100644 index 0000000..14d226d --- /dev/null +++ b/zh/qsl4a/connectivity/phone/index.html @@ -0,0 +1,3887 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 电话 - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + +

电话 API

+

控制电话通话和获取电话信息。

+

电话状态跟踪

+

startTrackingPhoneState()

+

开始跟踪电话状态变化。生成 'phone' 事件。

+
startTrackingPhoneState()
+
+

readPhoneState()

+

读取当前电话状态。

+
readPhoneState()
+
+

返回: 包含电话状态和来电号码的 Bundle

+

stopTrackingPhoneState()

+

停止跟踪电话状态。

+
stopTrackingPhoneState()
+
+

拨打电话

+

phoneCall()

+

通过 URI 呼叫联系人/电话号码。

+
phoneCall(uri)
+
+

参数: +- uri (str): 联系人 URI 或电话号码 URI

+

phoneCallNumber()

+

直接拨打电话号码。

+
phoneCallNumber(phone_number)
+
+

参数: +- phone_number (str): 要拨打的电话号码

+

phoneDial()

+

拨打号码(打开拨号器但不呼叫)。

+
phoneDial(uri)
+
+

参数: +- uri (str): 联系人 URI 或电话号码 URI

+

phoneDialNumber()

+

拨打电话号码(打开拨号器但不呼叫)。

+
phoneDialNumber(phone_number)
+
+

参数: +- phone_number (str): 电话号码

+

基站位置

+

getCellLocation()

+

获取当前基站位置。

+
getCellLocation()
+
+

返回: 包含基站位置数据的 JSONObject

+

getAllCellsLocation()

+

获取所有基站位置(适用于双卡设备)。

+
getAllCellsLocation()
+
+

返回: 基站位置的 JSONArray

+

网络信息

+

getNetworkOperator()

+

获取当前运营商的 MCC+MNC。

+
getNetworkOperator()
+
+

返回: 字符串(例如 '310260')

+

getNetworkOperatorName()

+

获取当前运营商的名称。

+
getNetworkOperatorName()
+
+

返回: 字符串(例如 'T-Mobile')

+

getNetworkType()

+

获取当前网络类型。

+
getNetworkType()
+
+

返回: 描述无线电技术的字符串(例如 'LTE', 'UMTS', 'GSM')

+

getPhoneType()

+

获取电话类型。

+
getPhoneType()
+
+

返回: 字符串(例如 'GSM', 'CDMA', 'SIP')

+

SIM 卡信息

+

getSimCountryIso()

+

获取 SIM 卡的 ISO 国家代码。

+
getSimCountryIso()
+
+

返回: 字符串(例如 'us')

+

getSimOperator()

+

获取 SIM 运营商的 MCC+MNC。

+
getSimOperator()
+
+

返回: 字符串(例如 '310260')

+

getSimOperatorName()

+

获取 SIM 运营商名称。

+
getSimOperatorName()
+
+

返回: 字符串(例如 'T-Mobile')

+

getSimSerialNumber()

+

获取 SIM 序列号。

+
getSimSerialNumber()
+
+

返回: 字符串 SIM 序列号

+

getSimState()

+

获取 SIM 卡状态。

+
getSimState()
+
+

返回: 描述 SIM 状态的字符串

+

getSubscriberId()

+

获取订阅者 ID。

+
getSubscriberId()
+
+

返回: 字符串订阅者 ID

+

语音邮件

+

getVoiceMailAlphaTag()

+

获取语音邮件字母标签。

+
getVoiceMailAlphaTag()
+
+

返回: 字符串语音邮件标签

+

getVoiceMailNumber()

+

获取语音邮件号码。

+
getVoiceMailNumber()
+
+

返回: 字符串语音邮件号码

+

设备信息

+

getDeviceId()

+

获取设备 ID(GSM 的 IMEI)。已废弃。

+
getDeviceId()
+
+

返回: 字符串设备 ID

+

getDeviceSoftwareVersion()

+

获取设备软件版本。

+
getDeviceSoftwareVersion()
+
+

返回: 字符串软件版本

+

getLine1Number()

+

获取线路 1 电话号码。

+
getLine1Number()
+
+

返回: 字符串电话号码

+

checkNetworkRoaming()

+

检查是否连接到漫游网络。

+
checkNetworkRoaming()
+
+

返回: 如果正在漫游则为 True

+

基站信息

+

getAllCellInfo()

+

获取所有基站的信息。

+
getAllCellInfo()
+
+

返回: 基站信息列表

+

setDataEnabled()

+

启用或禁用移动数据。

+
setDataEnabled(enabled)
+
+

参数: +- enabled (bool): True 启用,False 禁用

+

使用示例

+
import androidhelper
+
+droid = androidhelper.Android()
+
+# 获取网络信息
+operator = droid.getNetworkOperatorName().result
+print(f"Operator: {operator}")
+
+network_type = droid.getNetworkType().result
+print(f"Network: {network_type}")
+
+# 获取 SIM 信息
+sim_state = droid.getSimState().result
+print(f"SIM: {sim_state}")
+
+# 获取电话号码
+line1 = droid.getLine1Number().result
+print(f"Phone: {line1}")
+
+# 跟踪电话状态
+droid.startTrackingPhoneState()
+print("Tracking phone state...")
+droid.stopTrackingPhoneState()
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/zh/qsl4a/connectivity/signalstrength/index.html b/zh/qsl4a/connectivity/signalstrength/index.html new file mode 100644 index 0000000..86b611f --- /dev/null +++ b/zh/qsl4a/connectivity/signalstrength/index.html @@ -0,0 +1,3040 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 信号强度 - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

信号强度 API

+

监控蜂窝和无线信号强度。

+

信号强度方法

+

startTrackingSignalStrengths()

+

开始跟踪信号强度变化。生成 'signal_strengths' 事件。

+
startTrackingSignalStrengths()
+
+

stopTrackingSignalStrengths()

+

停止跟踪信号强度。

+
stopTrackingSignalStrengths()
+
+

readSignalStrengths()

+

读取当前信号强度。

+
readSignalStrengths()
+
+

返回: 包含信号强度数据的 Bundle

+

getTelephoneSignalStrengthLevel()

+

获取电话信号强度等级(0-4)。

+
getTelephoneSignalStrengthLevel()
+
+

返回: 整数等级(0=无,1=差,2=一般,3=好,4=优秀)

+

getTelephoneSignalStrengthDetail()

+

获取详细的电话信号强度信息。

+
getTelephoneSignalStrengthDetail()
+
+

返回: 包含详细信号信息的字符串

+

使用示例

+
import androidhelper
+import time
+
+droid = androidhelper.Android()
+
+# 开始跟踪信号强度
+droid.startTrackingSignalStrengths()
+
+# 等待信号更新
+time.sleep(5)
+
+# 读取当前信号强度
+signal = droid.readSignalStrengths().result
+print(f"Signal: {signal}")
+
+# 直接获取等级
+level = droid.getTelephoneSignalStrengthLevel().result
+print(f"Signal level: {level}/4")
+
+droid.stopTrackingSignalStrengths()
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/zh/qsl4a/connectivity/sms/index.html b/zh/qsl4a/connectivity/sms/index.html new file mode 100644 index 0000000..780df34 --- /dev/null +++ b/zh/qsl4a/connectivity/sms/index.html @@ -0,0 +1,3114 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 短信 - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

SMS API

+

发送和接收 SMS 消息。

+

方法

+

smsSend()

+

发送 SMS 消息。

+
smsSend(destinationAddress, text)
+
+

参数: +- destinationAddress (str): 电话号码 +- text (str): 消息文本

+

smsGetMessageCount()

+

获取消息数量。

+
smsGetMessageCount(unreadOnly=False, folder="inbox")
+
+

smsGetMessageIds()

+

获取消息 ID。

+
smsGetMessageIds(unreadOnly=False, folder="inbox")
+
+

smsGetMessages()

+

获取消息详情。

+
smsGetMessages(unreadOnly=False, folder="inbox", attributes=None)
+
+

smsGetMessageById()

+

通过 ID 获取特定消息。

+
smsGetMessageById(id, attributes=None)
+
+

参数: +- id (int): 消息 ID +- attributes (list, optional): 要检索的特定属性

+

返回: 消息数据字典

+

smsGetAttributes()

+

获取可用的 SMS 消息属性。

+
smsGetAttributes()
+
+

返回: 可用属性名称列表

+

smsDeleteMessage()

+

删除消息。

+
smsDeleteMessage(id)
+
+

smsMarkMessageRead()

+

将消息标记为已读。

+
smsMarkMessageRead(ids, read=True)
+
+

使用示例

+
import androidhelper
+
+droid = androidhelper.Android()
+
+# 发送 SMS
+droid.smsSend("+1234567890", "Hello from QPython!")
+
+# 获取未读消息
+messages = droid.smsGetMessages(unreadOnly=True).result
+for msg in messages:
+    print(f"From: {msg['address']}, Text: {msg['body']}")
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/zh/qsl4a/connectivity/wifi/index.html b/zh/qsl4a/connectivity/wifi/index.html new file mode 100644 index 0000000..0170117 --- /dev/null +++ b/zh/qsl4a/connectivity/wifi/index.html @@ -0,0 +1,3427 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + WiFi - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

WiFi API

+

控制 WiFi 适配器并获取连接信息。

+

适配器控制

+

checkWifiState()

+

检查 WiFi 是否已启用。

+
checkWifiState()
+
+

返回: 如果 WiFi 已启用则为 True,否则为 False

+

toggleWifiState()

+

打开或关闭 WiFi。

+
toggleWifiState(enabled=None)
+
+

参数: +- enabled (bool): True 启用,False 禁用,None 切换

+

返回: 如果操作成功则为 True

+

wifiStartScan()

+

开始扫描可用的 WiFi 网络。

+
wifiStartScan()
+
+

wifiGetScanResults()

+

获取发现的 WiFi 网络列表。

+
wifiGetScanResults()
+
+

返回: 接入点信息列表

+

连接管理

+

wifiGetConnectionInfo()

+

获取详细连接信息。

+
wifiGetConnectionInfo()
+
+

返回: 包含 SSID、BSSID、IP 地址等连接详情的字典

+

getConnectedInfo()

+

获取连接的 WiFi 网络信息(简化版)。

+
getConnectedInfo()
+
+

返回: 包含 SSID、BSSID、信号强度的字典

+

getDhcpInfo()

+

获取当前连接的 DHCP 信息。

+
getDhcpInfo(ipConvertToString=True)
+
+

参数: +- ipConvertToString (bool): 将 IP 地址转换为字符串格式(默认:True)

+

返回: 包含 IP、网关、DNS 等 DHCP 信息的字典

+

wifiDisconnect()

+

断开当前 WiFi 网络连接。

+
wifiDisconnect()
+
+

wifiReconnect()

+

重新连接到当前网络。

+
wifiReconnect()
+
+

wifiReassociate()

+

重新关联当前接入点。

+
wifiReassociate()
+
+

热点

+

wifiGetApState()

+

获取 WiFi AP(热点)状态。

+
wifiGetApState()
+
+

返回: 热点状态

+

WiFi 锁

+

wifiLockAcquireFull()

+

获取完整 WiFi 锁(即使屏幕关闭也保持 WiFi 活动)。

+
wifiLockAcquireFull()
+
+

wifiLockAcquireScanOnly()

+

获取仅扫描 WiFi 锁。

+
wifiLockAcquireScanOnly()
+
+

wifiLockRelease()

+

释放 WiFi 锁。

+
wifiLockRelease()
+
+

使用示例

+
import androidhelper
+import time
+
+droid = androidhelper.Android()
+
+# 检查 WiFi 状态
+if droid.checkWifiState().result:
+    print("WiFi is enabled")
+else:
+    print("Enabling WiFi...")
+    droid.toggleWifiState(True)
+    time.sleep(2)
+
+# 开始扫描
+droid.wifiStartScan()
+time.sleep(3)
+
+# 获取扫描结果
+networks = droid.wifiGetScanResults().result
+for network in networks:
+    print(f"SSID: {network.get('SSID')}, Signal: {network.get('level')} dBm")
+
+# 获取连接信息
+info = droid.wifiGetConnectionInfo().result
+if info:
+    print(f"Connected to: {info.get('ssid')}")
+    print(f"BSSID: {info.get('bssid')}")
+    print(f"IP: {info.get('ip_address')}")
+
+# 获取 DHCP 信息
+dhcp = droid.getDhcpInfo().result
+if dhcp:
+    print(f"Gateway: {dhcp.get('gateway')}")
+    print(f"DNS: {dhcp.get('dns1')}")
+
+# 获取简化的连接信息
+connected = droid.getConnectedInfo().result
+print(f"SSID: {connected.get('ssid')}, Signal: {connected.get('level')} dBm")
+
+# 断开并重新连接
+droid.wifiDisconnect()
+time.sleep(1)
+droid.wifiReconnect()
+
+# 重新关联接入点
+droid.wifiReassociate()
+
+# 检查热点状态
+ap_state = droid.wifiGetApState().result
+print(f"Hotspot state: {ap_state}")
+
+# 获取 WiFi 锁以进行后台操作
+droid.wifiLockAcquireFull()
+# ... 执行工作 ...
+droid.wifiLockRelease()
+
+# 或使用仅扫描锁以进行较轻的后台操作
+droid.wifiLockAcquireScanOnly()
+# ... 执行扫描工作 ...
+droid.wifiLockRelease()
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/zh/qsl4a/core/android-base/index.html b/zh/qsl4a/core/android-base/index.html new file mode 100644 index 0000000..796f476 --- /dev/null +++ b/zh/qsl4a/core/android-base/index.html @@ -0,0 +1,3286 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Android 基础 - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

Android 基础类

+

Android 类是 QSL4A 的核心,提供了与 Android 运行时的连接和 RPC 机制。

+

模块导入

+
# 功能完整版本(推荐)
+import androidhelper
+droid = androidhelper.Android()
+
+# 简化版本(快速,单实例)
+import android
+droid = android.droid
+
+

类:Android

+

构造函数

+
Android(addr=None)
+
+

参数: +- addr (tuple, optional): (HOST, PORT) 地址。如果为 None,则使用环境变量 AP_HOSTAP_PORT

+

环境变量: +- AP_HOST - 服务器主机地址 +- AP_PORT - 服务器端口 +- AP_HANDSHAKE - 认证令牌

+

核心方法

+

_rpc()

+

调用 Android 函数的内部 RPC 方法。

+
_rpc(method, *args)
+
+

参数: +- method (str): 要调用的方法名 +- *args: 方法的可变参数

+

返回: Result 命名元组,包含以下字段: +- id (int): 请求 ID +- result: 方法返回值 +- error (str or None): 错误信息(如果失败)

+

getattr()

+

动态方法分发器。所有 Android 方法都通过属性查找访问。

+
# 以下两种方式等价:
+droid.makeToast("Hello")
+droid._rpc("makeToast", "Hello")
+
+

独立函数(android.py)

+

使用简化的 android 模块时:

+

jsla()

+

发送 JSON-RPC 请求并返回原始响应。

+
jsla(method, *params)
+
+

返回: JSON 字符串响应

+

rsla()

+

发送请求并仅返回结果。

+
rsla(method, *params)
+
+

返回: RPC 调用的结果值

+

esla()

+

发送请求,错误时抛出异常。

+
esla(method, *params)
+
+

抛出: 如果 error 字段不为 None 则抛出异常

+

nsla()

+

发送请求并返回 Result 命名元组。

+
nsla(method, *params)
+
+

返回: Result 命名元组

+

使用示例

+

基本连接

+
import androidhelper
+
+# 连接到 Android 运行时
+droid = androidhelper.Android()
+
+# 通过显示 toast 来检查连接
+droid.makeToast("Connected!")
+
+

错误处理

+
result = droid.someMethod()
+if result.error:
+    print(f"Error: {result.error}")
+else:
+    print(f"Success: {result.result}")
+
+

直接 RPC 调用

+
# 直接调用任意方法
+result = droid._rpc("makeToast", "Hello World")
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/zh/qsl4a/core/events/index.html b/zh/qsl4a/core/events/index.html new file mode 100644 index 0000000..4506d2e --- /dev/null +++ b/zh/qsl4a/core/events/index.html @@ -0,0 +1,3654 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 事件系统 - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

事件系统

+

QSL4A 提供了一个事件系统,用于处理来自 Android 的异步事件,如传感器更新、位置变化和自定义广播。

+

事件基础

+

事件存储在缓冲区中,可以轮询或等待。每个事件包含: +- name: 事件类型/名称 +- data: 事件负载数据 +- time: 时间戳

+

事件方法

+

eventClearBuffer()

+

清除缓冲区中的所有待处理事件。

+
eventClearBuffer()
+
+

返回: None

+

eventPoll()

+

轮询缓冲区中的事件。

+
eventPoll(number_of_events=1)
+
+

参数: +- number_of_events (int): 要检索的最大事件数(默认:1)

+

返回: 事件对象列表

+

eventWait()

+

等待任意事件。

+
eventWait(timeout=None)
+
+

参数: +- timeout (int, optional): 超时时间(秒)。None = 永久等待

+

返回: 事件对象,如果超时则返回 None

+

eventWaitFor()

+

等待特定事件。

+
eventWaitFor(eventName, timeout=None)
+
+

参数: +- eventName (str): 要等待的事件名称 +- timeout (int, optional): 超时时间(秒)

+

返回: 事件对象,如果超时则返回 None

+

eventPost()

+

发布自定义事件。

+
eventPost(name, data, enqueue=None)
+
+

参数: +- name (str): 事件名称 +- data: 事件数据(任意类型) +- enqueue (bool, optional): 如果为 True 则添加到队列

+

receiveEvent()

+

接收事件(阻塞)。

+
receiveEvent()
+
+

返回: 事件对象

+

广播事件

+

注册系统广播事件。

+

eventRegisterForBroadcast()

+

注册接收广播事件。

+
eventRegisterForBroadcast(category, enqueue=True)
+
+

参数: +- category (str): 广播类别/动作 +- enqueue (bool): 添加到事件队列

+

eventUnregisterForBroadcast()

+

取消注册广播事件。

+
eventUnregisterForBroadcast(category)
+
+

eventGetBrodcastCategories()

+

获取已注册的广播类别。

+
eventGetBrodcastCategories()
+
+

返回: 已注册类别列表

+

事件分发器)

+

startEventDispatcher()

+

打开一个可以读取已发布事件的套接字。)

+
startEventDispatcher(port=0)
+
+

参数: +- port (int, optional): 监听的端口(默认:0 = 自动选择)

+

返回: 正在监听的端口号

+

stopEventDispatcher()

+

停止事件服务器。)

+
stopEventDispatcher()
+
+

已废弃方法

+

rpcPostEvent()

+

将事件发布到事件队列。(已废弃,请使用 eventPost)

+
rpcPostEvent(name, data)
+
+

参数: +- name (str): 事件名称 +- data: 事件数据

+

使用示例

+

基本事件轮询

+
import androidhelper
+
+droid = androidhelper.Android()
+
+# 清除旧事件
+droid.eventClearBuffer()
+
+# 轮询事件
+events = droid.eventPoll(5).result
+for event in events:
+    print(f"Event: {event['name']}, Data: {event['data']}")
+
+

等待事件

+
# 等待任意事件,带超时
+event = droid.eventWait(timeout=10).result
+if event:
+    print(f"Got event: {event['name']}")
+
+

等待特定事件

+
# 等待传感器事件
+event = droid.eventWaitFor('screen', timeout=5).result
+if event:
+    print(f"Screen event: {event['data']}")
+
+

发布自定义事件

+
# 发布自定义事件
+droid.eventPost('my_event', {'key': 'value'})
+
+# 等待它
+event = droid.eventWaitFor('my_event', timeout=1).result
+
+

广播接收器

+
# 注册屏幕开关事件
+droid.eventRegisterForBroadcast('android.intent.action.SCREEN_ON')
+droid.eventRegisterForBroadcast('android.intent.action.SCREEN_OFF')
+
+# 等待屏幕事件
+while True:
+    event = droid.receiveEvent().result
+    if event['name'] == 'android.intent.action.SCREEN_ON':
+        print("Screen turned on")
+    elif event['name'] == 'android.intent.action.SCREEN_OFF':
+        print("Screen turned off")
+
+

传感器事件处理

+
# 开始感知
+droid.startSensingTimed(1, 250)
+
+# 处理传感器事件
+for _ in range(100):
+    event = droid.eventWait(timeout=1).result
+    if event and event['name'] == 'sensors':
+        data = event['data']
+        print(f"Accel: {data['xforce']}, {data['yforce']}, {data['zforce']}")
+
+droid.stopSensing()
+
+

常见事件类型

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
事件名称描述来源
sensors传感器数据更新startSensing*
locationGPS 位置更新startLocating
phone电话状态变化startTrackingPhoneState
signal信号强度变化startTrackingSignalStrength
screen截图就绪fullGetScreenShot
dialog对话框响应dialogShow*
+

事件数据结构

+
{
+    'name': 'event_name',
+    'data': {
+        # 事件特定数据
+    },
+    'time': 1234567890  # 时间戳
+}
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/zh/qsl4a/core/intent/index.html b/zh/qsl4a/core/intent/index.html new file mode 100644 index 0000000..2d99197 --- /dev/null +++ b/zh/qsl4a/core/intent/index.html @@ -0,0 +1,3936 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Intent 系统 - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

Intent 系统

+

Android Intent 用于启动活动、发送广播和应用间通信。QSL4A 通过 Intent 模块提供完整的 Intent 支持。

+

模块导入

+
import androidhelper
+don = androidhelper.Android()
+
+

Intent 常量

+

通过 droid.Intent 访问:

+

操作

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
常量用途
ACTION_MAINandroid.intent.action.MAIN应用入口点
ACTION_VIEWandroid.intent.action.VIEW查看内容
ACTION_EDITandroid.intent.action.EDIT编辑内容
ACTION_PICKandroid.intent.action.PICK选择项目
ACTION_SENDandroid.intent.action.SEND分享内容
ACTION_SEARCHandroid.intent.action.SEARCH搜索
+

标志

+ + + + + + + + + + + + + + + + + + + + + + + + + +
常量用途
FLAG_ACTIVITY_NEW_TASK268435456启动新任务
FLAG_ACTIVITY_CLEAR_TASK32768清除任务堆栈
FLAG_ACTIVITY_NEW_DOCUMENT524288新文档模式
+

附加数据

+ + + + + + + + + + + + + + + + + + + + + + + + + +
常量用途
EXTRA_TEXT文本内容
EXTRA_STREAM文件 URI
EXTRA_SUBJECT主题行
EXTRA_EMAIL电子邮件地址
+

核心方法

+

makeIntent()

+

创建 Intent 对象。

+
makeIntent(action, uri=None, type=None, extras=None, categories=None,
+           packagename=None, classname=None, flags=None)
+
+

参数: +- action (str): Intent 操作(例如 droid.Intent.ACTION_VIEW) +- uri (str, optional): 数据 URI +- type (str, optional): MIME 类型 +- extras (dict, optional): 附加数据 +- categories (list, optional): Intent 类别 +- packagename (str, optional): 目标包 +- classname (str, optional): 目标类 +- flags (int, optional): Intent 标志

+

返回: Intent 对象

+

startActivityIntent()

+

使用 Intent 启动 Activity。

+
startActivityIntent(intent, wait=None)
+
+

参数: +- intent: makeIntent() 返回的 Intent 对象 +- wait (bool, optional): 阻塞直到活动关闭

+

startActivityForResultIntent()

+

启动活动并等待结果。

+
startActivityForResultIntent(intent)
+
+

返回: Activity 结果

+

sendBroadcastIntent()

+

发送广播。

+
sendBroadcastIntent(intent)
+
+

view()

+

通过 URI 查看内容。

+
view(uri, type=None, extras=None)
+
+

pick()

+

从 URI 选择内容。

+
pick(uri)
+
+

常见 Intent 方法)

+

scanBarcode()

+

启动条码扫描器。

+
scanBarcode()
+
+

返回: 扫描的条码字符串

+

send()

+

通过分享 Intent 发送内容。

+
send(type, content)
+
+

参数: +- type (str): MIME 类型 +- content (str): 要分享的内容

+

sendText()

+

发送文本内容。

+
sendText(text)
+
+

参数: +- text (str): 要发送的文本

+

sendEmail()

+

发送电子邮件。

+
sendEmail(to, subject, body, attachment=None)
+
+

参数: +- to (str or list): 收件人电子邮件地址 +- subject (str): 电子邮件主题 +- body (str): 电子邮件正文 +- attachment (str, optional): 附件文件路径

+

pathToUri()

+

将文件路径转换为内容 URI。

+
pathToUri(path)
+
+

参数: +- path (str): 文件路径

+

返回: 内容 URI 字符串

+

openFile()

+

用适当的应用程序打开文件。

+
openFile(path)
+
+

参数: +- path (str): 要打开的文件路径

+

sendFile()

+

通过分享 Intent 发送文件。

+
sendFile(path)
+
+

参数: +- path (str): 要发送的文件路径

+

getPathType()

+

获取文件路径的 MIME 类型。

+
getPathType(path)
+
+

参数: +- path (str): 文件路径

+

返回: MIME 类型字符串

+

viewMap()

+

在指定位置打开地图。

+
viewMap(latitude, longitude)
+
+

参数: +- latitude (float): 纬度 +- longitude (float): 经度

+

viewContacts()

+

打开联系人应用。

+
viewContacts()
+
+ +

执行网络搜索。

+
search(query)
+
+

参数: +- query (str): 搜索查询

+

viewHtml()

+

查看 HTML 内容。

+
viewHtml(content, encoding=None)
+
+

参数: +- content (str): HTML 内容 +- encoding (str, optional): 字符编码

+

webViewShow()

+

在 WebView 中显示网页内容。已废弃,请使用 viewHtml。

+
webViewShow(url)
+
+

参数: +- url (str): 网页 URL

+

editorOpen()

+

打开文本编辑器。

+
editorOpen(path=None, create=False)
+
+

参数: +- path (str, optional): 要编辑的文件路径 +- create (bool, optional): 如果不存在则创建

+

辅助类:Uri

+

创建 Intent 的 URI 对象:

+
from androidhelper.Intent import Uri
+
+uri = Uri("file:///sdcard/test.txt")
+
+

使用示例

+

打开网页

+
intent = droid.makeIntent(
+    action=droid.Intent.ACTION_VIEW,
+    uri="http://www.example.com"
+).result
+droid.startActivityIntent(intent)
+
+

分享文本

+
intent = droid.makeIntent(
+    action=droid.Intent.ACTION_SEND,
+    extras={
+        droid.Intent.EXTRA_TEXT: "Hello from QPython!",
+        droid.Intent.EXTRA_SUBJECT: "Test"
+    },
+    type="text/plain"
+).result
+droid.startActivityIntent(intent)
+
+

打开文件

+
intent = droid.makeIntent(
+    action=droid.Intent.ACTION_VIEW,
+    uri="file:///sdcard/document.pdf",
+    type="application/pdf"
+).result
+droid.startActivityIntent(intent)
+
+

选择联系人

+
result = droid.pickContact()
+contact_uri = result.result
+
+

发送电子邮件

+
intent = droid.makeIntent(
+    action=droid.Intent.ACTION_SEND,
+    extras={
+        droid.Intent.EXTRA_EMAIL: ["test@example.com"],
+        droid.Intent.EXTRA_SUBJECT: "Hello",
+        droid.Intent.EXTRA_TEXT: "Message body"
+    },
+    type="message/rfc822"
+).result
+droid.startActivityIntent(intent)
+
+

打开应用

+
intent = droid.makeIntent(
+    action=droid.Intent.ACTION_MAIN,
+    packagename="com.android.settings",
+    classname="com.android.settings.Settings"
+).result
+droid.startActivityIntent(intent)
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/zh/qsl4a/hardware/bluetooth/index.html b/zh/qsl4a/hardware/bluetooth/index.html new file mode 100644 index 0000000..2791180 --- /dev/null +++ b/zh/qsl4a/hardware/bluetooth/index.html @@ -0,0 +1,3549 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 蓝牙 - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

蓝牙 API

+

控制蓝牙适配器并与蓝牙设备通信。

+

适配器控制

+

toggleBluetoothState()

+

打开/关闭蓝牙。

+
toggleBluetoothState(enabled=None, prompt=True)
+
+

参数: +- enabled (bool): True=开,False=关,None=切换 +- prompt (bool): 显示用户提示

+

checkBluetoothState()

+

检查蓝牙是否已启用。

+
checkBluetoothState()
+
+

返回: True/False

+

GetLocalName()

+

获取蓝牙设备名称。

+
GetLocalName()
+
+

SetLocalName()

+

设置蓝牙设备名称。

+
SetLocalName(name)
+
+

GetScanMode()

+

获取可发现性模式。

+
GetScanMode()
+
+

返回: +- -1: 已禁用 +- 0: 不可发现,不可连接 +- 1: 可连接,不可发现 +- 3: 可连接且可发现

+

MakeDiscoverable()

+

使设备可发现。

+
MakeDiscoverable(duration=300)
+
+

参数: +- duration (int): 可发现的秒数

+

发现

+

DiscoveryStart()

+

开始设备发现。

+
DiscoveryStart()
+
+

DiscoveryCancel()

+

取消发现。

+
DiscoveryCancel()
+
+

GetReceivedDevices()

+

获取发现的设备。

+
GetReceivedDevices()
+
+

返回: 设备信息字典列表

+

GetBondedDevices()

+

获取配对设备。

+
GetBondedDevices()
+
+

返回: 配对设备信息列表

+

连接

+

Connect()

+

连接到设备。

+
Connect(uuid="457807c0-4897-11df-9879-0800200c9a66", address=None)
+
+

参数: +- uuid (str): 服务 UUID +- address (str): 设备地址(None = 显示选择器)

+

返回: 如果成功则为 True

+

Accept()

+

接受传入连接。

+
Accept(uuid="457807c0-4897-11df-9879-0800200c9a66", timeout=0)
+
+

ActiveConnections()

+

检查活动连接。

+
ActiveConnections()
+
+

Stop()

+

断开连接。

+
Stop(connID=None)
+
+

通信

+

Write()

+

发送 ASCII 数据。

+
Write(ascii, connID="")
+
+

WriteBinary()

+

发送二进制数据(base64 编码)。

+
WriteBinary(base64, connID=None)
+
+

Read()

+

读取 ASCII 数据。

+
Read(bufferSize=4096, connID=None)
+
+

ReadBinary()

+

读取二进制数据。

+
ReadBinary(bufferSize=4096, connID=None)
+
+

ReadLine()

+

读取一行。

+
ReadLine(connID=None)
+
+

ReadReady()

+

检查是否有可用数据。

+
ReadReady(connID=None)
+
+

使用示例

+
import androidhelper
+
+droid = androidhelper.Android()
+
+# 启用蓝牙
+droid.toggleBluetoothState(True)
+
+# 获取配对设备
+devices = droid.GetBondedDevices().result
+for dev in devices:
+    print(f"{dev['name']}: {dev['address']}")
+
+# 连接到设备
+droid.Connect(address="00:11:22:33:44:55")
+
+# 发送数据
+droid.Write("Hello Bluetooth!")
+
+# 读取响应
+data = droid.Read(1024).result
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/zh/qsl4a/hardware/camera/index.html b/zh/qsl4a/hardware/camera/index.html new file mode 100644 index 0000000..af30047 --- /dev/null +++ b/zh/qsl4a/hardware/camera/index.html @@ -0,0 +1,3393 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 相机 - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

相机 API

+

拍摄照片和录制视频。

+

照片拍摄

+

takePicture()

+

使用默认相机拍摄照片。

+
takePicture(path=None)
+
+

参数: +- path (str, optional): 保存路径。如果为 None,则返回图像数据

+

返回: 图像路径或图像数据

+

cameraCapturePicture()

+

使用高级相机控制拍摄照片。

+
cameraCapturePicture(targetPath=None, cameraId=0, useAutoFocus=True)
+
+

参数: +- targetPath (str, optional): 保存路径 +- cameraId (int): 使用的相机(0 = 后置,1 = 前置) +- useAutoFocus (bool): 启用自动对焦

+

返回: 拍摄的图像路径

+

cameraSetTorchMode()

+

控制相机闪光灯/手电筒。

+
cameraSetTorchMode(enabled)
+
+

参数: +- enabled (bool): True 打开,False 关闭

+

返回: 如果成功则为 True

+

imageReaderGetScreenShot()

+

截图。

+
imageReaderGetScreenShot(path=None, delayMilliSec=1000)
+
+

参数: +- path (str, optional): 保存路径 +- delayMilliSec (int): 拍摄前延迟

+

视频录制

+

takeVideo()

+

使用默认设置录制视频。

+
takeVideo(path=None, quality=1)
+
+

参数: +- path (str, optional): 保存路径 +- quality (int): 视频质量(0-4) + - 0: 160x120 + - 1: 320x240 + - 2: 352x288 + - 3: 640x480 + - 4: 800x480

+

recorderCaptureVideo()

+

使用高级控制录制视频。

+
recorderCaptureVideo(targetPath=None, duration=10, cameraId=0, quality=8)
+
+

参数: +- targetPath (str, optional): 保存路径 +- duration (int): 录制时长(秒)(默认:10) +- cameraId (int): 使用的相机(0 = 后置,1 = 前置) +- quality (int): 视频质量(0-8,越高越好)

+

返回: 视频文件路径

+

recordAudio()

+

录制音频。

+
recordAudio()
+
+

返回: 音频文件路径

+

屏幕录制

+

recorderStartScreenRecord()

+

开始屏幕录制。

+
recorderStartScreenRecord(path=None, audio=1, targetPixels=None,
+                          frameRate=30, bitRate=None, rotation=False,
+                          autoStart=True)
+
+

参数: +- path (str): 保存路径 +- audio (int): 音频源(0=无,1=麦克风,2=内部) +- targetPixels (int): 分辨率 +- frameRate (int): FPS +- bitRate (int): 比特率 +- rotation (bool): 旋转输出 +- autoStart (bool): 立即开始

+

recorderStart()

+

开始录制。

+
recorderStart()
+
+

recorderPause()

+

暂停录制。

+
recorderPause()
+
+

recorderResume()

+

恢复录制。

+
recorderResume()
+
+

音频音量检测

+

recorderSoundVolumeDetect()

+

开始音量检测。

+
recorderSoundVolumeDetect(interval=100)
+
+

recorderSoundVolumeGetDb()

+

获取当前音量(分贝)。

+
recorderSoundVolumeGetDb()
+
+

使用示例

+
import androidhelper
+
+droid = androidhelper.Android()
+
+# 使用默认相机拍照
+photo_path = droid.takePicture("/sdcard/photo.jpg").result
+print(f"Photo saved: {photo_path}")
+
+# 使用前置相机和自动对焦拍照
+camera_path = droid.cameraCapturePicture("/sdcard/selfie.jpg", cameraId=1, useAutoFocus=True).result
+print(f"Front camera photo: {camera_path}")
+
+# 控制闪光灯
+droid.cameraSetTorchMode(True)  # 打开闪光灯
+
+# 使用默认设置录制视频
+video_path = droid.takeVideo("/sdcard/video.mp4", quality=3).result
+print(f"Video saved: {video_path}")
+
+# 使用高级控制录制视频
+video_path = droid.recorderCaptureVideo("/sdcard/movie.mp4", duration=30, cameraId=0, quality=8).result
+
+# 截图
+ss_path = droid.imageReaderGetScreenShot("/sdcard/screenshot.png", 500).result
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/zh/qsl4a/hardware/recorder/index.html b/zh/qsl4a/hardware/recorder/index.html new file mode 100644 index 0000000..0cd1b58 --- /dev/null +++ b/zh/qsl4a/hardware/recorder/index.html @@ -0,0 +1,3213 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 音频/录音 - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

音频录制 API

+

从麦克风和设备屏幕录制音频。

+

音频录制

+

recordAudio()

+

从麦克风录制音频。

+
recordAudio()
+
+

返回: 录制的音频文件路径

+

recorderStartMicrophone()

+

开始从麦克风录制到特定文件。

+
recorderStartMicrophone(targetPath=None)
+
+

参数: +- targetPath (str, optional): 保存录制的路径

+

屏幕录制

+

recorderStartScreenRecord()

+

开始带音频的屏幕录制。

+
recorderStartScreenRecord(path=None, audio=1, targetPixels=None,
+                          frameRate=30, bitRate=None, rotation=False,
+                          autoStart=True)
+
+

参数: +- path (str): 视频文件保存路径 +- audio (int): 音频源(0=无,1=麦克风,2=内部音频) +- targetPixels (int): 目标分辨率(像素) +- frameRate (int): 每秒帧数(默认:30) +- bitRate (int): 视频比特率 +- rotation (bool): 旋转输出视频 +- autoStart (bool): 立即开始录制

+

返回: 操作结果

+

recorderStart()

+

开始屏幕录制(当 autoStart=False 时)。

+
recorderStart()
+
+

recorderPause()

+

暂停正在进行的屏幕录制。

+
recorderPause()
+
+

recorderResume()

+

恢复暂停的屏幕录制。

+
recorderResume()
+
+

音频音量检测

+

recorderSoundVolumeDetect()

+

开始监控音量级别。

+
recorderSoundVolumeDetect(interval=100)
+
+

参数: +- interval (int): 检测间隔(毫秒)(默认:100)

+

recorderSoundVolumeGetDb()

+

获取当前音量(分贝)。

+
recorderSoundVolumeGetDb()
+
+

返回: 当前音量级别(dB)

+

使用示例

+
import androidhelper
+import time
+
+droid = androidhelper.Android()
+
+# 从麦克风录制音频
+print("Recording audio...")
+audio_path = droid.recordAudio().result
+print(f"Saved to: {audio_path}")
+
+# 录制到特定文件
+droid.recorderStartMicrophone("/sdcard/my_recording.mp3")
+time.sleep(5)
+droid.recorderStop()
+
+# 带音频录制屏幕
+print("Starting screen recording...")
+droid.recorderStartScreenRecord(
+    path="/sdcard/screen_record.mp4",
+    audio=1,
+    frameRate=30,
+    autoStart=True
+)
+time.sleep(10)
+droid.recorderStop()
+
+# 监控音量
+droid.recorderSoundVolumeDetect(interval=100)
+time.sleep(3)
+volume = droid.recorderSoundVolumeGetDb().result
+print(f"Current volume: {volume} dB")
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/zh/qsl4a/hardware/usbserial/index.html b/zh/qsl4a/hardware/usbserial/index.html new file mode 100644 index 0000000..68476a0 --- /dev/null +++ b/zh/qsl4a/hardware/usbserial/index.html @@ -0,0 +1,3284 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + USB 串口 - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

USB 主机串行 API

+

与 USB 串行设备通信(需要 USB OTG 支持和 Android 3.1+)。

+

USB 串行方法)

+

usbHostSerialOpen()

+

打开与 USB 串行设备的连接。

+
usbHostSerialOpen(device, baudRate=9600)
+
+

参数: +- device (str): USB 设备路径或标识符 +- baudRate (int): 波特率(默认:9600)

+

返回: 如果打开成功则为 True

+

usbHostSerialClose()

+

关闭 USB 串行连接。

+
usbHostSerialClose()
+
+

usbHostSerialRead()

+

从 USB 串行读取数据。

+
usbHostSerialRead(bufferSize=1024)
+
+

参数: +- bufferSize (int): 要读取的最大字节数(默认:1024)

+

返回: 读取的数据字符串

+

usbHostSerialWrite()

+

向 USB 串行写入数据。

+
usbHostSerialWrite(data)
+
+

参数: +- data (str): 要写入的字符串数据

+

usbHostSerialAvailable()

+

检查是否有可读取的数据。

+
usbHostSerialAvailable()
+
+

返回: 可用字节数

+

配置方法)

+

usbHostSerialSetBaudRate()

+

设置波特率。

+
usbHostSerialSetBaudRate(baudRate)
+
+

参数: +- baudRate (int): 波特率

+

usbHostSerialSetDataBits()

+

设置数据位(5、6、7 或 8)。

+
usbHostSerialSetDataBits(dataBits)
+
+

参数: +- dataBits (int): 数据位(5-8)

+

usbHostSerialSetStopBits()

+

设置停止位(1、1.5 或 2)。

+
usbHostSerialSetStopBits(stopBits)
+
+

参数: +- stopBits (float): 停止位(1、1.5 或 2)

+

usbHostSerialSetParity()

+

设置校验位(无、奇、偶、标记、空格)。

+
usbHostSerialSetParity(parity)
+
+

参数: +- parity (str): 校验模式('none'、'odd'、'even'、'mark'、'space')

+

usbHostSerialSetFlowControl()

+

设置流控制(无、硬件、软件)。

+
usbHostSerialSetFlowControl(flowControl)
+
+

参数: +- flowControl (str): 流控制模式('none'、'hardware'、'software')

+

usbHostSerialReadHex()

+

以十六进制字符串读取数据。

+
usbHostSerialReadHex(bufferSize=1024)
+
+

参数: +- bufferSize (int): 要读取的最大字节数

+

返回: 十六进制字符串

+

usbHostSerialWriteHex()

+

从十六进制字符串写入数据。

+
usbHostSerialWriteHex(hexString)
+
+

参数: +- hexString (str): 要写入的十六进制字符串

+

使用示例

+
import androidhelper
+
+droid = androidhelper.Android()
+
+# 打开 USB 串行连接
+if droid.usbHostSerialOpen("/dev/bus/usb/001/001", 115200).result:
+    print("USB serial opened")
+
+    # 写入数据
+    droid.usbHostSerialWrite("AT\r")
+
+    # 读取响应
+    response = droid.usbHostSerialRead(1024).result
+    print(f"Response: {response}")
+
+    # 或使用十六进制
+    droid.usbHostSerialWriteHex("41540D0A")  # "AT\r\n"
+
+    # 关闭连接
+    droid.usbHostSerialClose()
+
+

注意: USB 串行需要: +- Android 3.1+ (API 12) +- USB OTG 线/适配器 +- 设备上的 USB 主机模式支持 +- 兼容的串行设备

+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/zh/qsl4a/hardware/webcam/index.html b/zh/qsl4a/hardware/webcam/index.html new file mode 100644 index 0000000..b2461c9 --- /dev/null +++ b/zh/qsl4a/hardware/webcam/index.html @@ -0,0 +1,3097 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 网络摄像头 - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

网络摄像头 API

+

使用 MJPEG 从设备相机流式传输视频。

+

MJPEG 流方法

+

webcamStart()

+

从网络摄像头启动 MJPEG 流。

+
webcamStart(resolutionLevel=0, jpegQuality=20, port=0)
+
+

参数: +- resolutionLevel (int): 分辨率级别(默认:0) +- jpegQuality (int): JPEG 质量 1-100(默认:20) +- port (int): 端口号(默认:0 = 自动)

+

返回: 流的 (地址, 端口) 元组

+

webcamAdjustQuality()

+

调整活动网络摄像头流的质量。

+
webcamAdjustQuality(resolutionLevel=0, jpegQuality=20)
+
+

参数: +- resolutionLevel (int): 分辨率级别 +- jpegQuality (int): JPEG 质量 1-100

+

webcamStop()

+

停止网络摄像头流。

+
webcamStop()
+
+

相机预览方法

+

cameraStartPreview()

+

开始带事件生成的相机预览模式。

+
cameraStartPreview(resolutionLevel=0, jpegQuality=20, filepath=None)
+
+

参数: +- resolutionLevel (int): 分辨率级别(默认:0) +- jpegQuality (int): JPEG 质量(默认:20) +- filepath (str, optional): 保存预览帧的文件路径

+

返回: 如果成功则为 True

+

注意: 生成带有帧数据的 'preview' 事件。

+

cameraStopPreview()

+

停止相机预览。

+
cameraStopPreview()
+
+

使用示例

+
import androidhelper
+import time
+
+droid = androidhelper.Android()
+
+# 启动网络摄像头流
+stream_info = droid.webcamStart(
+    resolutionLevel=0,
+    jpegQuality=30,
+    port=8080
+).result
+print(f"Stream available at {stream_info[0]}:{stream_info[1]}")
+
+# 流式传输时调整质量
+time.sleep(5)
+droid.webcamAdjustQuality(resolutionLevel=1, jpegQuality=50)
+
+# 完成后停止
+droid.webcamStop()
+
+# 或使用预览模式
+print("Starting preview...")
+droid.cameraStartPreview()
+
+# 等待预览事件
+for i in range(10):
+    event = droid.eventWait(timeout=1).result
+    if event and event['name'] == 'preview':
+        print(f"Got preview frame: {event['data']}")
+
+droid.cameraStopPreview()
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/zh/qsl4a/index.html b/zh/qsl4a/index.html new file mode 100644 index 0000000..7d42438 --- /dev/null +++ b/zh/qsl4a/index.html @@ -0,0 +1,3170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 概览 - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+ +
+
+ + + +
+ +
+ + + + + +

QSL4A(Android 脚本层)API 文档

+

QSL4A 是 QPython 的 Android 脚本层,允许您使用 Python 控制 Android 设备功能。

+

快速开始

+
import androidhelper
+
+droid = androidhelper.Android()
+
+# 显示 toast 消息
+droid.makeToast('Hello QPython!')
+
+# 震动设备
+droid.vibrate(500)
+
+# 获取电池电量(先开始监控)
+import time
+droid.batteryStartMonitoring()
+time.sleep(0.5)  # 等待数据
+battery = droid.readBatteryData().result
+print(f"电池:{battery['level']}%")
+
+

文档结构

+

核心模块

+ +

UI 组件

+ +

系统

+ +

硬件

+ +

连接

+ +

存储

+ +

媒体

+ +

特殊功能

+ +

结果对象

+

大多数 QSL4A 方法返回一个带有以下属性的 Result 命名元组: +- id - 请求 ID +- result - 实际结果数据 +- error - 如果失败则为错误消息

+
result = droid.getClipboard()
+if result.error is None:
+    print(result.result)
+else:
+    print(f"错误:{result.error}")
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/zh/qsl4a/media/image/index.html b/zh/qsl4a/media/image/index.html new file mode 100644 index 0000000..ef894d8 --- /dev/null +++ b/zh/qsl4a/media/image/index.html @@ -0,0 +1,3139 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 图像处理 - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + +

图像处理 API

+

压缩和处理图像。

+

图像压缩

+

imageCompress()

+

压缩图像文件。

+
imageCompress(srcPath, destPath, targetByteSize=0, targetWidth=0, targetHeight=0)
+
+

参数: +- srcPath (str): 源图像路径 +- destPath (str): 输出路径 +- targetByteSize (int): 目标文件大小(字节)(0 = 无限制) +- targetWidth (int): 目标宽度(0 = 原始) +- targetHeight (int): 目标高度(0 = 原始)

+

返回: 压缩后的图像路径

+

截图

+

imageReaderGetScreenShot()

+

截取屏幕。

+
imageReaderGetScreenShot(path=None, delayMilliSec=1000)
+
+

参数: +- path (str): 保存路径 +- delayMilliSec (int): 拍摄前延迟

+

返回: 截图路径

+

视频播放

+

videoPlay()

+

在全屏模式下播放视频文件。

+
videoPlay(path, wait=True)
+
+

参数: +- path (str): 视频文件路径 +- wait (bool): 等待播放完成(默认:True)

+

条码扫描

+

scanBarcodeFromImage()

+

从图像文件扫描条码/二维码。

+
scanBarcodeFromImage(path, compressRatio=0, x=0, y=0, width=0, height=0)
+
+

参数: +- path (str): 图像文件路径 +- compressRatio (int): 压缩比(0 = 不压缩) +- x, y, width, height (int): 要扫描的区域(0 = 完整图像)

+

返回: 扫描的条码内容

+

使用示例

+
import androidhelper
+
+droid = androidhelper.Android()
+
+# 截图
+ss_path = droid.imageReaderGetScreenShot("/sdcard/screenshot.png", 500).result
+
+# 压缩图像
+compressed = droid.imageCompress(
+    "/sdcard/large_photo.jpg",
+    "/sdcard/compressed.jpg",
+    targetByteSize=102400,  # 目标约 100KB
+    targetWidth=1920,
+    targetHeight=1080
+).result
+print(f"Saved: {compressed}")
+
+# 播放视频
+video_path = "/sdcard/movie.mp4"
+droid.videoPlay(video_path, wait=True)
+
+# 从图像扫描条码
+result = droid.scanBarcodeFromImage("/sdcard/qr_code.png").result
+print(f"Barcode: {result}")
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/zh/qsl4a/media/mediaplayer/index.html b/zh/qsl4a/media/mediaplayer/index.html new file mode 100644 index 0000000..55813c2 --- /dev/null +++ b/zh/qsl4a/media/mediaplayer/index.html @@ -0,0 +1,3388 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 媒体播放器 - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

媒体播放器 API

+

控制音频和视频播放。

+

播放控制

+

mediaPlay()

+

播放媒体文件。

+
mediaPlay(url, tag="default", play=True)
+
+

参数: +- url (str): 媒体文件路径或 URL +- tag (str): 播放器标识符 +- play (bool): 自动开始播放

+

mediaPlayStart()

+

开始播放。

+
mediaPlayStart(tag="default")
+
+

mediaPlayPause()

+

暂停播放。

+
mediaPlayPause(tag="default")
+
+

mediaPlayClose()

+

关闭播放器。

+
mediaPlayClose(tag="default")
+
+

mediaPlaySeek()

+

跳转到位置。

+
mediaPlaySeek(msec, tag="default")
+
+

参数: +- msec (int): 位置(毫秒)

+

mediaPlaySetLooping()

+

设置循环模式。

+
mediaPlaySetLooping(enabled, tag="default")
+
+

播放器信息

+

mediaPlayInfo()

+

获取播放信息。

+
mediaPlayInfo(tag="default")
+
+

返回: 包含时长、位置等的字典

+

mediaIsPlaying()

+

检查是否正在播放。

+
mediaIsPlaying(tag="default")
+
+

返回: True/False

+

mediaPlayList()

+

列出活动播放器。

+
mediaPlayList()
+
+

音量控制

+

getMediaVolume()

+

获取媒体音量。

+
getMediaVolume()
+
+

返回: 音量级别(0-15)

+

getMaxMediaVolume()

+

获取最大媒体音量。

+
getMaxMediaVolume()
+
+

getRingerVolume()

+

获取铃声音量。

+
getRingerVolume()
+
+

getMaxRingerVolume()

+

获取最大铃声音量。

+
getMaxRingerVolume()
+
+

视频播放

+

videoPlay()

+

全屏播放视频。

+
videoPlay(path, wait=True)
+
+

参数: +- path (str): 视频文件路径 +- wait (bool): 等待播放完成

+

使用示例

+
import androidhelper
+
+droid = androidhelper.Android()
+
+# 播放音频
+droid.mediaPlay("/sdcard/music.mp3", tag="music")
+
+# 检查状态
+if droid.mediaIsPlaying("music").result:
+    info = droid.mediaPlayInfo("music").result
+    print(f"Playing: {info}")
+
+# 跳转到 30 秒
+droid.mediaPlaySeek(30000, "music")
+
+# 关闭
+droid.mediaPlayClose("music")
+
+# 播放视频
+droid.videoPlay("/sdcard/movie.mp4", wait=True)
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/zh/qsl4a/special/cipher/index.html b/zh/qsl4a/special/cipher/index.html new file mode 100644 index 0000000..933eaae --- /dev/null +++ b/zh/qsl4a/special/cipher/index.html @@ -0,0 +1,3265 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 加密 - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

密码 API

+

用于安全数据存储的加密和解密工具。

+

初始化

+

cipherInit()

+

使用加密密钥和算法初始化密码器。

+
cipherInit(key, algorithm="AES/CBC/PKCS5Padding", encodingFormat="", initialVector="")
+
+

参数: +- key (str or bytes): 加密密钥 +- algorithm (str): 密码算法(默认:"AES/CBC/PKCS5Padding") +- encodingFormat (str): 编码格式 +- initialVector (str or bytes): 加密的初始向量

+

返回: 初始化结果

+

注意: 必须在任何加密/解密操作之前调用。

+

加密方法

+

encryptString()

+

加密字符串。

+
encryptString(plainText)
+
+

参数: +- plainText (str): 要加密的文本

+

返回: 加密的字符串

+

encryptBytes()

+

加密字节数据。

+
encryptBytes(data)
+
+

参数: +- data (bytes): 要加密的数据

+

返回: 加密的字节

+

encryptStringToFile()

+

加密字符串到文件。

+
encryptStringToFile(plainText, filePath)
+
+

参数: +- plainText (str): 要加密的文本 +- filePath (str): 输出文件路径

+

encryptBytesToFile()

+

加密字节到文件。

+
encryptBytesToFile(data, filePath)
+
+

解密方法

+

decryptString()

+

解密为字符串。

+
decryptString(cipherText)
+
+

参数: +- cipherText (str): 加密的文本

+

返回: 解密的字符串

+

decryptBytes()

+

解密为字节。

+
decryptBytes(data)
+
+

返回: 解密的字节

+

decryptFileToString()

+

解密文件到字符串。

+
decryptFileToString(filePath)
+
+

decryptFileToBytes()

+

解密文件到字节。

+
decryptFileToBytes(filePath)
+
+

decryptFile()

+

解密文件到文件。

+
decryptFile(srcPath, destPath)
+
+

使用示例

+
import androidhelper
+
+droid = androidhelper.Android()
+
+# 使用加密密钥初始化密码器
+droid.cipherInit("my_secret_key_1234")
+
+# 加密字符串
+encrypted = droid.encryptString("Secret message!").result
+print(f"Encrypted: {encrypted}")
+
+# 解密字符串
+decrypted = droid.decryptString(encrypted).result
+print(f"Decrypted: {decrypted}")
+
+# 加密到文件
+droid.encryptStringToFile("My secret data", "/sdcard/secret.dat")
+
+# 从文件解密
+data = droid.decryptFileToString("/sdcard/secret.dat").result
+print(data)
+
+# 使用自定义算法和 IV 初始化
+droid.cipherInit(
+    key="my_key",
+    algorithm="AES/CBC/PKCS5Padding",
+    initialVector="0123456789abcdef"
+)
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/zh/qsl4a/special/pgptai/index.html b/zh/qsl4a/special/pgptai/index.html new file mode 100644 index 0000000..2304149 --- /dev/null +++ b/zh/qsl4a/special/pgptai/index.html @@ -0,0 +1,3099 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + PGPT AI - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + +

PGPT AI API

+

语音转文本和 AI 服务集成。

+

先决条件

+
pip install pgptAI
+
+

语音识别

+

speechToText()

+

将语音转换为文本。

+
speechToText(RecordSecond=10, AmrFile=None, Language=None)
+
+

参数: +- RecordSecond (int): 录制时长(秒) +- AmrFile (str, optional): 现有音频文件路径 +- Language (str, optional): 语言代码('en'、'zh')

+

返回: 转录的文本

+

文本转语音

+

textToSpeech()

+

将文本转换为语音并可选地播放。

+
textToSpeech(Text, AutoPlay=True, WavFile=None, VoiceName=None)
+
+

参数: +- Text (str): 要转换为语音的文本 +- AutoPlay (bool): 自动播放生成的音频(默认:True) +- WavFile (str, optional): 保存 WAV 文件的路径。如果为 None,则使用临时文件 +- VoiceName (str, optional): 要使用的语音名称(例如 'en-US-JennyNeural'、'zh-CN-XiaoxiaoNeural')

+

返回: 包含语音合成结果的字典,包括: + - text: 输入文本 + - url: 下载音频文件的 URL + - WavFile: 保存的 WAV 文件路径(如果本地保存)

+

配置

+

API 使用以下配置文件中的配置:/storage/emulated/0/Android/data/org.qpython.qpy/files/apigpt.conf

+
[speech]
+speech_key = your_api_key
+
+

默认语音设置: +- 英语:en-US-JennyNeural +- 中文:zh-CN-XiaoxiaoNeural

+

使用示例

+
import androidhelper
+
+droid = androidhelper.Android()
+
+# 录制并转录
+print("Recording for 5 seconds...")
+text = droid.speechToText(RecordSecond=5, Language='en').result
+print(f"You said: {text}")
+
+# 转录现有文件
+text = droid.speechToText(AmrFile="/sdcard/recording.amr").result
+print(f"Transcription: {text}")
+
+# 文本转语音
+droid.textToSpeech("Hello, this is a test message.", AutoPlay=True, Language='en')
+
+# 使用自定义语音并保存到文件的文本转语音
+result = droid.textToSpeech(
+    "Welcome to QPython!",
+    AutoPlay=False,
+    WavFile="/sdcard/welcome.wav",
+    VoiceName="en-US-JennyNeural"
+).result
+print(f"Audio saved to: {result.get('WavFile')}")
+
+

类使用

+
from androidhelper.pgptai import pgptai
+import androidhelper
+
+droid = androidhelper.Android()
+ai = pgptai(droid)
+
+# 使用语音识别
+text = ai.speechToText(RecordSecond=10)
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/zh/qsl4a/storage/clipboard/index.html b/zh/qsl4a/storage/clipboard/index.html new file mode 100644 index 0000000..4c2e5ab --- /dev/null +++ b/zh/qsl4a/storage/clipboard/index.html @@ -0,0 +1,2953 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 剪贴板 - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + +

剪贴板 API

+

复制和粘贴文本到系统剪贴板。

+

方法

+

setClipboard()

+

复制文本到剪贴板。

+
setClipboard(text)
+
+

参数: +- text (str): 要复制的文本

+

返回: 如果成功则为 True

+

getClipboard()

+

从剪贴板获取文本。

+
getClipboard()
+
+

返回: 剪贴板文本

+

使用示例

+
import androidhelper
+
+droid = androidhelper.Android()
+
+# 复制到剪贴板
+droid.setClipboard("Hello from QPython!")
+
+# 从剪贴板粘贴
+text = droid.getClipboard().result
+print(f"Clipboard: {text}")
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/zh/qsl4a/storage/documentfile/index.html b/zh/qsl4a/storage/documentfile/index.html new file mode 100644 index 0000000..314031a --- /dev/null +++ b/zh/qsl4a/storage/documentfile/index.html @@ -0,0 +1,3478 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + DocumentFile - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

DocumentFile API

+

使用 SAF(存储访问框架)进行文件操作,支持 Android 4.4+。

+

目录操作

+

documentFileMkdir()

+

创建目录。

+
documentFileMkdir(Dir)
+
+

参数: +- Dir (str): 目录路径

+

返回: 如果成功则为 True

+

documentFileListFiles()

+

列出目录中的文件。

+
documentFileListFiles(Folder)
+
+

返回: 文件列表

+

文件操作

+

documentFileExists()

+

检查文件或目录是否存在。

+
documentFileExists(path)
+
+

参数: +- path (str): 文件或目录路径

+

返回: 如果存在则为 True,否则为 False

+

documentFileIsFile()

+

检查路径是否是文件。

+
documentFileIsFile(path)
+
+

参数: +- path (str): 要检查的路径

+

返回: 如果是文件则为 True,如果不是文件则为 False,如果不存在则为 None

+

documentFileIsDirectory()

+

检查路径是否是目录。

+
documentFileIsDirectory(path)
+
+

参数: +- path (str): 要检查的路径

+

返回: 如果是目录则为 True,如果不是目录则为 False,如果不存在则为 None

+

documentFileDelete()

+

删除文件或目录。

+
documentFileDelete(FileOrTree)
+
+

返回: 如果成功则为 True

+

documentFileRenameTo()

+

重命名或移动文件。

+
documentFileRenameTo(Src, Dest)
+
+

返回: 如果成功则为 True

+

documentFileCopy()

+

复制文件。

+
documentFileCopy(SrcFileOrTree, DestFileOrTree)
+
+

流操作

+

documentFileInputStream()

+

读取文件内容。

+
documentFileInputStream(srcFile, EncodingFormat="", skip=None, length=None)
+
+

参数: +- srcFile (str): 源文件 +- EncodingFormat (str): "UTF-8"、"GBK"、"Base64" 或空字符串表示字节 +- skip (int): 从开头跳过的字节数 +- length (int): 读取长度

+

返回: 文件内容

+

documentFileOutputStream()

+

写入文件内容。

+
documentFileOutputStream(destFile, src, EncodingFormat="", append=None)
+
+

参数: +- destFile (str): 目标文件 +- src: 要写入的数据 +- EncodingFormat (str): 编码格式 +- append (bool): 追加模式

+

文件信息

+

documentFileLength()

+

获取文件大小(字节)。

+
documentFileLength(path)
+
+

参数: +- path (str): 文件路径

+

返回: 文件大小(字节)(如果不存在则为 0)

+

documentFileLastModified()

+

获取最后修改时间。

+
documentFileLastModified(path)
+
+

参数: +- path (str): 文件路径

+

返回: 时间戳(如果不存在则为 0)

+

documentFileGetStat()

+

获取全面的文件统计信息。

+
documentFileGetStat(path)
+
+

参数: +- path (str): 文件路径

+

返回: 包含长度、最后修改时间和读/写权限的字典,如果不存在则为 None

+

URI 操作

+

documentFileGetUri()

+

从路径获取 URI。

+
documentFileGetUri(path, isDirectory=None)
+
+

documentFileShowOpen()

+

显示文件选择器。

+
documentFileShowOpen()
+
+

返回: 选择的文件 URI

+

使用示例

+
import androidhelper
+
+droid = androidhelper.Android()
+
+# 创建目录
+droid.documentFileMkdir("/sdcard/MyFolder")
+
+# 列出文件
+files = droid.documentFileListFiles("/sdcard").result
+for f in files:
+    print(f)
+
+# 读取文件
+content = droid.documentFileInputStream(
+    "/sdcard/test.txt",
+    EncodingFormat="UTF-8"
+).result
+print(content)
+
+# 写入文件
+droid.documentFileOutputStream(
+    "/sdcard/output.txt",
+    "Hello World",
+    EncodingFormat="UTF-8"
+)
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/zh/qsl4a/storage/preferences/index.html b/zh/qsl4a/storage/preferences/index.html new file mode 100644 index 0000000..8b0b6b8 --- /dev/null +++ b/zh/qsl4a/storage/preferences/index.html @@ -0,0 +1,3028 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 偏好设置 - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + +

偏好设置 API

+

使用 Android SharedPreferences 存储和检索数据。

+

偏好设置方法

+

prefGetValue()

+

从共享偏好设置读取值。

+
prefGetValue(key, filename=None)
+
+

参数: +- key (str): 偏好设置键 +- filename (str, optional): 偏好设置文件名

+

返回: 存储的值(任意类型)

+

prefPutValue()

+

写入值到共享偏好设置。

+
prefPutValue(key, value, filename=None)
+
+

参数: +- key (str): 偏好设置键 +- value (object): 要存储的值 +- filename (str, optional): 偏好设置文件名

+

prefGetAll()

+

获取所有偏好设置值。

+
prefGetAll(filename=None)
+
+

参数: +- filename (str, optional): 偏好设置文件名

+

返回: 所有偏好的映射

+

prefRemoveValue()

+

从共享偏好设置中移除值。

+
prefRemoveValue(key, filename=None)
+
+

参数: +- key (str): 要移除的偏好设置键 +- filename (str, optional): 偏好设置文件名

+

使用示例

+
import androidhelper
+
+droid = androidhelper.Android()
+
+# 存储值
+droid.prefPutValue("username", "alice")
+droid.prefPutValue("score", 100)
+droid.prefPutValue("enabled", True)
+
+# 读取特定值
+username = droid.prefGetValue("username").result
+print(f"Username: {username}")
+
+# 获取所有偏好设置
+all_prefs = droid.prefGetAll().result
+print(f"All prefs: {all_prefs}")
+
+# 移除值
+droid.prefRemoveValue("score")
+
+# 使用自定义文件名
+droid.prefPutValue("token", "abc123", filename="auth.prefs")
+token = droid.prefGetValue("token", filename="auth.prefs").result
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/zh/qsl4a/system/activityresult/index.html b/zh/qsl4a/system/activityresult/index.html new file mode 100644 index 0000000..58e0bc5 --- /dev/null +++ b/zh/qsl4a/system/activityresult/index.html @@ -0,0 +1,3449 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Activity 结果 - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

Activity Result API

+

为通过 startActivityForResult 启动的脚本设置 activity 结果。

+

结果方法

+

setResultBoolean()

+

设置布尔结果。

+
setResultBoolean(resultCode, resultValue)
+
+

参数: +- resultCode (int): 结果代码 +- resultValue (bool): 布尔结果值

+

setResultByte()

+

设置字节结果。

+
setResultByte(resultCode, resultValue)
+
+

参数: +- resultCode (int): 结果代码 +- resultValue (int): 字节结果值

+

setResultShort()

+

设置短整数结果。

+
setResultShort(resultCode, resultValue)
+
+

参数: +- resultCode (int): 结果代码 +- resultValue (int): 短整数结果值

+

setResultChar()

+

设置字符结果。

+
setResultChar(resultCode, resultValue)
+
+

参数: +- resultCode (int): 结果代码 +- resultValue (str): 字符结果值

+

setResultInteger()

+

设置整数结果。

+
setResultInteger(resultCode, resultValue)
+
+

参数: +- resultCode (int): 结果代码 +- resultValue (int): 整数结果值

+

setResultLong()

+

设置长整数结果。

+
setResultLong(resultCode, resultValue)
+
+

参数: +- resultCode (int): 结果代码 +- resultValue (int): 长整数结果值

+

setResultFloat()

+

设置浮点数结果。

+
setResultFloat(resultCode, resultValue)
+
+

参数: +- resultCode (int): 结果代码 +- resultValue (float): 浮点数结果值

+

setResultDouble()

+

设置双精度浮点数结果。

+
setResultDouble(resultCode, resultValue)
+
+

参数: +- resultCode (int): 结果代码 +- resultValue (float): 双精度浮点数结果值

+

setResultString()

+

设置字符串结果。

+
setResultString(resultCode, resultValue)
+
+

参数: +- resultCode (int): 结果代码 +- resultValue (str): 字符串结果值

+

setResultBooleanArray()

+

设置布尔数组结果。

+
setResultBooleanArray(resultCode, resultValue)
+
+

参数: +- resultCode (int): 结果代码 +- resultValue (list): 布尔数组

+

setResultByteArray()

+

设置字节数组结果。

+
setResultByteArray(resultCode, resultValue)
+
+

参数: +- resultCode (int): 结果代码 +- resultValue (list): 字节数组

+

setResultShortArray()

+

设置短整数数组结果。

+
setResultShortArray(resultCode, resultValue)
+
+

参数: +- resultCode (int): 结果代码 +- resultValue (list): 短整数数组

+

setResultCharArray()

+

设置字符数组结果。

+
setResultCharArray(resultCode, resultValue)
+
+

参数: +- resultCode (int): 结果代码 +- resultValue (list): 字符数组

+

setResultIntegerArray()

+

设置整数数组结果。

+
setResultIntegerArray(resultCode, resultValue)
+
+

参数: +- resultCode (int): 结果代码 +- resultValue (list): 整数数组

+

setResultLongArray()

+

设置长整数数组结果。

+
setResultLongArray(resultCode, resultValue)
+
+

参数: +- resultCode (int): 结果代码 +- resultValue (list): 长整数数组

+

setResultFloatArray()

+

设置浮点数数组结果。

+
setResultFloatArray(resultCode, resultValue)
+
+

参数: +- resultCode (int): 结果代码 +- resultValue (list): 浮点数数组

+

setResultDoubleArray()

+

设置双精度浮点数数组结果。

+
setResultDoubleArray(resultCode, resultValue)
+
+

参数: +- resultCode (int): 结果代码 +- resultValue (list): 双精度浮点数数组

+

setResultStringArray()

+

设置字符串数组结果。

+
setResultStringArray(resultCode, resultValue)
+
+

参数: +- resultCode (int): 结果代码 +- resultValue (list): 字符串数组

+

setResultSerializable()

+

设置可序列化结果。

+
setResultSerializable(resultCode, resultValue)
+
+

参数: +- resultCode (int): 结果代码 +- resultValue: 可序列化结果值

+

使用示例

+
import androidhelper
+
+droid = androidhelper.Android()
+
+# 执行 activity 后,设置结果
+# 示例:返回成功及数据
+droid.setResultInteger(0, 200)  # RESULT_OK
+droid.setResultString(0, "Operation completed successfully")
+
+# 返回数组结果
+droid.setResultIntegerArray(0, [1, 2, 3, 4, 5])
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/zh/qsl4a/system/application/index.html b/zh/qsl4a/system/application/index.html new file mode 100644 index 0000000..13216c4 --- /dev/null +++ b/zh/qsl4a/system/application/index.html @@ -0,0 +1,3750 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 应用 - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

应用管理

+

管理应用程序、启动应用和查询系统信息。

+

应用程序信息

+

getApplicationInfo()

+

获取应用信息。

+
getApplicationInfo(packageName=None)
+
+

参数: +- packageName (str): 包名(None = 当前应用)

+

返回: 应用信息字典

+

getInstalledPackages()

+

获取已安装包列表。

+
getInstalledPackages(flag=4)
+
+

参数: +- flag (int): 包标志过滤器(默认:4)

+

返回: 已安装包列表

+

getRunningPackages()

+

列出正在运行的包。

+
getRunningPackages()
+
+

返回: 包名列表

+

getLaunchablePackages()

+

获取可启动包列表。

+
getLaunchablePackages(needClassName=False)
+
+

参数: +- needClassName (bool): 包含主 activity 类名(默认:False)

+

返回: 可启动包名列表或包含类名的字典

+

应用控制

+

launch()

+

启动应用程序。

+
launch(classname=None, packagename=None, wait=True)
+
+

参数: +- classname (str): 主 activity 类名 +- packagename (str): 包名 +- wait (bool): 等待启动完成(默认:True)

+

返回: 启动结果

+

forceStopPackage()

+

强制停止应用程序。

+
forceStopPackage(packageName)
+
+

参数: +- packageName (str): 要停止的包名

+

版本信息

+

getPackageVersion()

+

获取应用版本名称。

+
getPackageVersion(packageName)
+
+

返回: 版本字符串(例如 "3.2.1")

+

getPackageVersionCode()

+

获取应用版本代码。

+
getPackageVersionCode(packageName)
+
+

返回: 版本代码整数

+

getConstants()

+

获取类常量。

+
getConstants(classname)
+
+

参数: +- classname (str): 完整类名

+

返回: 常量名和值的字典

+

系统功能

+

backgroundProtect()

+

启用或禁用应用的后台保护。

+
backgroundProtect(enabled=True)
+
+

参数: +- enabled (bool): True 启用保护,False 禁用

+

createScriptShortCut()

+

为脚本创建主屏幕快捷方式。

+
createScriptShortCut(scriptPath, label=None, iconPath=None, scriptArg=None)
+
+

参数: +- scriptPath (str): Python 脚本路径 +- label (str, optional): 快捷方式标签 +- iconPath (str, optional): 图标图像路径 +- scriptArg (str, optional): 传递给脚本的参数

+

设备信息

+

getAndroidID()

+

获取 Android 设备 ID。

+
getAndroidID()
+
+

返回: 唯一的 Android 设备 ID 字符串

+

getSysInfo()

+

获取系统信息。

+
getSysInfo()
+
+

返回: 包含系统详情的字典

+

getLocale()

+

获取设备语言环境。

+
getLocale()
+
+

返回: 语言环境字符串(例如 "en_US")

+

getHarmonyOsInformation()

+

如果在 HarmonyOS 上运行,获取 HarmonyOS 信息。

+
getHarmonyOsInformation()
+
+

返回: HarmonyOS 版本信息或 None

+

isExternalStorageManager()

+

检查应用是否有外部存储管理器权限。

+
isExternalStorageManager()
+
+

返回: 如果有权限则为 True

+

内存和显示

+

getMemoryInfo()

+

获取内存信息。

+
getMemoryInfo()
+
+

返回: 包含内存统计的字典

+

getScreenInfo()

+

获取屏幕信息。

+
getScreenInfo()
+
+

返回: 包含宽度、高度、密度的字典

+

权限

+

checkPermissions()

+

检查当前应用权限。

+
checkPermissions()
+
+

返回: 权限及其状态的字典

+

requestPermissions()

+

向用户请求权限。

+
requestPermissions(permissions=None)
+
+

参数: +- permissions (list, optional): 要请求的权限列表。如果为 None,则请求所有需要的权限。

+

返回: 权限请求结果

+

系统操作

+

showScreenLock()

+

显示屏幕锁定(PIN/图案/密码输入)。

+
showScreenLock()
+
+

使用示例

+
import androidhelper
+
+droid = androidhelper.Android()
+
+# 获取系统信息
+info = droid.getSysInfo().result
+print(f"System: {info}")
+
+# 获取应用版本
+version = droid.getPackageVersion("org.qpython.qpy").result
+print(f"QPython version: {version}")
+
+# 列出已安装应用
+apps = droid.getInstalledPackages().result
+print(f"Installed apps: {len(apps)}")
+
+# 启动应用
+droid.launch(packagename="com.android.settings")
+
+# 检查权限
+perms = droid.checkPermissions().result
+print(f"Permissions: {perms}")
+
+# 请求存储权限
+droid.requestPermissions(["android.permission.READ_EXTERNAL_STORAGE"])
+
+# 创建快捷方式
+droid.createScriptShortCut(
+    "/sdcard/my_script.py",
+    label="My Script",
+    iconPath="/sdcard/icon.png"
+)
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/zh/qsl4a/system/battery/index.html b/zh/qsl4a/system/battery/index.html new file mode 100644 index 0000000..e9728aa --- /dev/null +++ b/zh/qsl4a/system/battery/index.html @@ -0,0 +1,3096 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 电池 - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

电池 API

+

监控设备电池状态和健康状况。

+

方法

+

readBatteryData()

+

获取完整的电池信息。

+
readBatteryData()
+
+

返回: 包含电池数据的字典

+

batteryStartMonitoring()

+

开始电池监控。

+
batteryStartMonitoring()
+
+

batteryStopMonitoring()

+

停止电池监控。

+
batteryStopMonitoring()
+
+

batteryGetLevel()

+

获取电池百分比。

+
batteryGetLevel()
+
+

返回: 整数(0-100)

+

batteryGetStatus()

+

获取充电状态。

+
batteryGetStatus()
+
+

返回: +- 1: 未知 +- 2: 充电中 +- 3: 放电中 +- 4: 未充电 +- 5: 已充满

+

batteryGetPlugType()

+

获取电源类型。

+
batteryGetPlugType()
+
+

返回: +- -1: 未知 +- 0: 未插电 +- 1: AC 充电器 +- 2: USB 端口

+

batteryGetHealth()

+

获取电池健康状况。

+
batteryGetHealth()
+
+

返回: +- 1: 未知 +- 2: 良好 +- 3: 过热 +- 4: 损坏 +- 5: 过压 +- 6: 未指定的故障

+

使用示例

+
import androidhelper
+
+droid = androidhelper.Android()
+
+# 获取电池电量
+level = droid.batteryGetLevel().result
+print(f"Battery: {level}%")
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/zh/qsl4a/system/qpyinterface/index.html b/zh/qsl4a/system/qpyinterface/index.html new file mode 100644 index 0000000..f6646df --- /dev/null +++ b/zh/qsl4a/system/qpyinterface/index.html @@ -0,0 +1,3177 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + QPython 接口 - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

QPython 接口 API

+

从其他应用执行 QPython 脚本和管理共享变量。

+

脚本执行方法

+

executeQPy()

+

执行 QPython 脚本。

+
executeQPy(path="", arg=None)
+
+

参数: +- path (str): 脚本文件路径 +- arg (str, optional): 命令行参数

+

返回: 如果启动成功则为 True

+

executeQPyAsSrv()

+

作为服务执行 QPython 脚本。

+
executeQPyAsSrv(path=None)
+
+

参数: +- path (str, optional): 脚本文件路径

+

返回: 如果启动成功则为 True

+

executeQPyCode()

+

直接执行 Python 代码。

+
executeQPyCode(code=None)
+
+

参数: +- code (str, optional): 要执行的 Python 代码

+

返回: 如果启动成功则为 True

+

executeQPyCodeAsSrv()

+

作为服务执行 Python 代码。

+
executeQPyCodeAsSrv(code=None)
+
+

参数: +- code (str, optional): 要执行的 Python 代码

+

返回: 如果启动成功则为 True

+

共享变量

+

共享变量允许 QPython 与其他应用之间进行通信。

+

sharedVariableSet()

+

设置 Java 共享变量。

+
sharedVariableSet(key, value)
+
+

参数: +- key (str): 变量名 +- value (str): 变量值

+

返回: 存储的值

+

sharedVariableGet()

+

获取 Java 共享变量。

+
sharedVariableGet(key)
+
+

参数: +- key (str): 变量名

+

返回: 存储的值

+

sharedVariableRemove()

+

移除 Java 共享变量。

+
sharedVariableRemove(key)
+
+

参数: +- key (str): 要移除的变量名

+

返回: 被移除的值

+

getLastLog()

+

获取 QPython 的最后日志输出。

+
getLastLog()
+
+

返回: 字符串日志内容

+

使用示例

+
import androidhelper
+
+droid = androidhelper.Android()
+
+# 执行脚本
+droid.executeQPy("/sdcard/my_script.py", arg="test")
+
+# 直接执行代码
+code = "print('Hello from QPython!')"
+droid.executeQPyCode(code)
+
+# 使用共享变量
+droid.sharedVariableSet("username", "alice")
+username = droid.sharedVariableGet("username").result
+print(f"Username: {username}")
+
+# 移除变量
+droid.sharedVariableRemove("username")
+
+# 获取最近日志
+log = droid.getLastLog().result
+print(f"Log: {log}")
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/zh/qsl4a/system/sensors/index.html b/zh/qsl4a/system/sensors/index.html new file mode 100644 index 0000000..4b1f577 --- /dev/null +++ b/zh/qsl4a/system/sensors/index.html @@ -0,0 +1,3203 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 传感器 - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

传感器 API

+

访问设备传感器,包括加速度计、陀螺仪、磁力计等。

+

方法

+

startSensingTimed()

+

开始传感器监控并设置时间间隔。

+
startSensingTimed(sensorNumber, delayTime)
+
+

参数: +- sensorNumber (int): 传感器 ID(通常为 1-3) +- delayTime (int): 读取之间的延迟(毫秒)

+

startSensingThreshold()

+

开始带阈值触发的传感器监控。

+
startSensingThreshold(sensorNumber, threshold, axis)
+
+

参数: +- sensorNumber (int): 传感器 ID +- threshold (float): 触发阈值 +- axis (int): 要监控的轴(0=X,1=Y,2=Z)

+

stopSensing()

+

停止所有传感器监控。

+
stopSensing()
+
+

readSensors()

+

读取当前传感器数据。

+
readSensors()
+
+

返回: 传感器数据字典

+

sensorsReadAccelerometer()

+

读取加速度计值。

+
sensorsReadAccelerometer()
+
+

返回: 列表 [X, Y, Z],单位 m/s²

+

sensorsReadGyroscope()

+

读取陀螺仪值。

+
sensorsReadGyroscope()
+
+

返回: 列表 [X, Y, Z],单位 rad/s

+

sensorsReadMagnetometer()

+

读取磁场值。

+
sensorsReadMagnetometer()
+
+

返回: 列表 [X, Y, Z],单位 μT

+

sensorsReadOrientation()

+

读取设备方向。

+
sensorsReadOrientation()
+
+

返回: 列表 [azimuth, pitch, roll],单位度

+

sensorsGetLight()

+

读取光传感器值。

+
sensorsGetLight()
+
+

返回: 光级别(lux)

+

sensorsGetStepCounter()

+

读取步计数器。

+
sensorsGetStepCounter()
+
+

返回: 步数

+

sensorsGetAccuracy()

+

获取当前传感器精度。

+
sensorsGetAccuracy()
+
+

返回: 精度值(0-3:UNRELIABLE、ACCURACY_LOW、ACCURACY_MEDIUM、ACCURACY_HIGH)

+

使用示例

+
import androidhelper
+import time
+
+droid = androidhelper.Android()
+
+# 开始感知
+droid.startSensingTimed(1, 250)
+
+# 读取传感器 10 次
+for i in range(10):
+    accel = droid.sensorsReadAccelerometer().result
+    print(f"Accel: X={accel[0]:.2f}, Y={accel[1]:.2f}, Z={accel[2]:.2f}")
+    time.sleep(0.5)
+
+droid.stopSensing()
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/zh/qsl4a/system/settings/index.html b/zh/qsl4a/system/settings/index.html new file mode 100644 index 0000000..17734ff --- /dev/null +++ b/zh/qsl4a/system/settings/index.html @@ -0,0 +1,3578 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 设置 - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

设置 API

+

控制系统设置,包括屏幕、声音和网络设置。

+

屏幕设置

+

setScreenTimeout()

+

设置屏幕超时值。

+
setScreenTimeout(value)
+
+

参数: +- value (int): 屏幕超时时间(秒)

+

返回: 之前的超时值

+

getScreenTimeout()

+

获取当前屏幕超时。

+
getScreenTimeout()
+
+

返回: 当前屏幕超时时间(秒)

+

getScreenBrightness()

+

获取屏幕亮度值。

+
getScreenBrightness()
+
+

返回: 亮度值(0-255)

+

setScreenBrightness()

+

设置屏幕亮度。

+
setScreenBrightness(value=None)
+
+

参数: +- value (int, optional): 亮度值(0-255),或 None 表示自动

+

返回: 之前的亮度值

+

checkScreenOn()

+

检查屏幕是否亮着。

+
checkScreenOn()
+
+

返回: 如果屏幕亮着则为 True,否则为 False

+

飞行模式

+

checkAirplaneMode()

+

检查飞行模式是否启用。

+
checkAirplaneMode()
+
+

返回: 如果飞行模式开着则为 True

+

铃声设置

+

checkRingerSilentMode()

+

检查铃声是否处于静音模式。

+
checkRingerSilentMode()
+
+

返回: 如果静音模式开着则为 True

+

toggleRingerSilentMode()

+

切换铃声静音模式。

+
toggleRingerSilentMode(enabled=None)
+
+

参数: +- enabled (bool, optional): True 启用,False 禁用,None 切换

+

返回: 新状态

+

toggleVibrateMode()

+

切换振动模式。

+
toggleVibrateMode(enabled=None, ringer=None)
+
+

参数: +- enabled (bool, optional): 切换振动开/关 +- ringer (bool, optional): 应用于铃声模式

+

返回: 新状态

+

getVibrateMode()

+

获取振动模式设置。

+
getVibrateMode(ringer=None)
+
+

参数: +- ringer (bool, optional): 检查铃声振动模式

+

返回: 如果振动已启用则为 True

+

音量设置

+

getRingerVolume()

+

获取当前铃声音量。

+
getRingerVolume()
+
+

返回: 铃声音量级别(通常为 0-7)

+

getMaxRingerVolume()

+

获取最大铃声音量。

+
getMaxRingerVolume()
+
+

返回: 最大铃声音量

+

setRingerVolume()

+

设置铃声音量。

+
setRingerVolume(volume)
+
+

参数: +- volume (int): 音量级别

+

getMediaVolume()

+

获取当前媒体音量。

+
getMediaVolume()
+
+

返回: 媒体音量级别(通常为 0-15)

+

getMaxMediaVolume()

+

获取最大媒体音量。

+
getMaxMediaVolume()
+
+

返回: 最大媒体音量

+

setMediaVolume()

+

设置媒体音量。

+
setMediaVolume(volume)
+
+

参数: +- volume (int): 音量级别

+

系统信息

+

elapsedRealtimeNanos()

+

获取自系统启动以来的纳秒数。

+
elapsedRealtimeNanos()
+
+

返回: 纳秒数(可用于计时)

+

getTrafficStats()

+

获取网络流量统计。

+
getTrafficStats(flags=7)
+
+

参数: +- flags (int): 要检索的统计(默认:7 = 全部)

+

返回: 包含发送/接收字节的字典

+

getAppTxBytes()

+

获取 QPython 应用的发送字节。

+
getAppTxBytes(packageName)
+
+

参数: +- packageName (str): 包名

+

返回: 包含 tx/rx 字节的字典

+

使用示例

+
import androidhelper
+
+droid = androidhelper.Android()
+
+# 屏幕设置
+current_timeout = droid.getScreenTimeout().result
+print(f"Current timeout: {current_timeout}s")
+droid.setScreenTimeout(30)
+
+# 检查屏幕
+if droid.checkScreenOn().result:
+    print("Screen is on")
+
+# 音量控制
+media_vol = droid.getMediaVolume().result
+print(f"Media volume: {media_vol}")
+droid.setMediaVolume(10)
+
+# 检查飞行模式
+if droid.checkAirplaneMode().result:
+    print("Airplane mode is on")
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/zh/qsl4a/system/sysinfo/index.html b/zh/qsl4a/system/sysinfo/index.html new file mode 100644 index 0000000..780d6cb --- /dev/null +++ b/zh/qsl4a/system/sysinfo/index.html @@ -0,0 +1,3194 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 系统信息 - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + +

系统信息

+

检索设备和系统信息。

+

设备信息

+

getAndroidID()

+

获取 Android 设备 ID。

+
getAndroidID()
+
+

返回: 字符串设备 ID

+

getSysInfo()

+

获取综合系统信息。

+
getSysInfo()
+
+

返回: 包含系统详情的字典

+

getLocale()

+

获取设备语言环境设置。

+
getLocale()
+
+

返回: 语言环境字符串(例如 "en_US")

+

内存

+

getMemoryInfo()

+

获取 RAM 信息。

+
getMemoryInfo()
+
+

返回: 包含内存统计的字典

+

显示

+

getScreenInfo()

+

获取显示信息。

+
getScreenInfo()
+
+

返回: 包含宽度、高度、密度的字典

+

标识符

+

getImei()

+

获取设备 IMEI。

+
getImei(slotIndex=None)
+
+

getMeid()

+

获取设备 MEID。

+
getMeid(slotIndex=None)
+
+

使用示例

+
import androidhelper
+
+droid = androidhelper.Android()
+
+# 设备 ID
+android_id = droid.getAndroidID().result
+print(f"Android ID: {android_id}")
+
+# 屏幕信息
+screen = droid.getScreenInfo().result
+print(f"Screen: {screen['width']}x{screen['height']}")
+
+# 内存
+memory = droid.getMemoryInfo().result
+print(f"Memory: {memory}")
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/zh/qsl4a/system/wakelock/index.html b/zh/qsl4a/system/wakelock/index.html new file mode 100644 index 0000000..b820e80 --- /dev/null +++ b/zh/qsl4a/system/wakelock/index.html @@ -0,0 +1,3088 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 唤醒锁 - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

WakeLock API

+

控制设备唤醒锁以保持 CPU 或屏幕开启。

+

Wake Lock 类型

+

QSL4A 提供不同类型的唤醒锁:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
类型描述
FullCPU 开启,屏幕亮,键盘亮
Partial仅 CPU 开启
BrightCPU 开启,屏幕亮
DimCPU 开启,屏幕暗
+

Wake Lock 方法

+

wakeLockAcquireFull()

+

获取完整唤醒锁(CPU 开启,屏幕亮,键盘亮)。

+
wakeLockAcquireFull()
+
+

wakeLockAcquirePartial()

+

获取部分唤醒锁(仅 CPU 开启)。

+
wakeLockAcquirePartial()
+
+

wakeLockAcquireBright()

+

获取明亮唤醒锁(CPU 开启,屏幕亮)。

+
wakeLockAcquireBright()
+
+

wakeLockAcquireDim()

+

获取暗淡唤醒锁(CPU 开启,屏幕暗)。

+
wakeLockAcquireDim()
+
+

wakeLockRelease()

+

释放唤醒锁。

+
wakeLockRelease()
+
+

使用示例

+
import androidhelper
+import time
+
+droid = androidhelper.Android()
+
+# 获取完整唤醒锁
+droid.wakeLockAcquireFull()
+
+# 在保持屏幕开启时执行重要工作
+print("Screen will stay on")
+time.sleep(10)
+
+# 完成后释放
+droid.wakeLockRelease()
+
+# 或使用部分锁进行后台任务
+droid.wakeLockAcquirePartial()
+# 即使屏幕关闭,CPU 仍保持开启
+time.sleep(30)
+droid.wakeLockRelease()
+
+

注意: 记住在不需要时释放唤醒锁以节省电池。

+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/zh/qsl4a/ui/accessibility/index.html b/zh/qsl4a/ui/accessibility/index.html new file mode 100644 index 0000000..0c247be --- /dev/null +++ b/zh/qsl4a/ui/accessibility/index.html @@ -0,0 +1,3246 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 辅助功能 - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

无障碍服务

+

无障碍服务允许自动化 UI 交互,如点击、滑动和系统操作。

+

服务控制

+

accessibilityStartService()

+

启动无障碍服务。

+
accessibilityStartService()
+
+

返回: 如果成功则为 True,否则为 False

+

accessibilityServiceEnabled()

+

检查无障碍服务是否已启用。

+
accessibilityServiceEnabled()
+
+

返回: TrueFalse

+

屏幕交互

+

accessibilityClick()

+

在屏幕坐标处点击。

+
accessibilityClick(x=0, y=0, t=50)
+
+

参数: +- x (int/float): X 坐标(0=居中,支持小数) +- y (int/float): Y 坐标(0=居中,支持小数) +- t (int): 按住时长(毫秒)(默认:50)

+

accessibilitySlide()

+

多点滑动手势。

+
accessibilitySlide(XnYn=None, t=None)
+
+

参数: +- XnYn (list): 坐标 [X1, Y1, X2, Y2, ... Xn, Yn] +- t (int): 滑动时长(默认:50*n 毫秒)

+

系统操作

+

accessibilityAction()

+

按代码执行系统操作。

+
accessibilityAction(actionCode)
+
+

操作代码: +| 代码 | 常量 | 描述 | +|------|----------|-------------| +| 1 | BACK | 返回按钮 | +| 2 | HOME | 主屏幕按钮 | +| 3 | RECENTS | 最近应用 | +| 4 | NOTIFICATIONS | 打开通知 | +| 5 | QUICK_SETTINGS | 打开快速设置 | +| 6 | POWER_DIALOG | 电源菜单 | +| 7 | TOGGLE_SPLIT_SCREEN | 分屏 | +| 8 | LOCK_SCREEN | 锁屏 | +| 9 | TAKE_SCREENSHOT | 截图 | +| 10 | KEYCODE_HEADSETHOOK | 耳机挂钩 | +| 11-14 | ACCESSIBILITY_ | 无障碍按钮 | +| 15 | DISMISS_NOTIFICATION_SHADE | 关闭通知栏 | +| 16-20 | DPAD_ | 方向键 | +| 21 | MENU | 菜单按钮 | +| 22 | MEDIA_PLAY_PAUSE | 播放/暂停媒体 |

+

使用示例

+

点击屏幕中心

+
import androidhelper
+
+droid = androidhelper.Android()
+
+# 启动服务
+if not droid.accessibilityServiceEnabled().result:
+    droid.accessibilityStartService()
+
+# 点击中心(0,0 = 中心)
+droid.accessibilityClick(0, 0, t=100)
+
+

滑动手势

+
# 从下往上滑动(向上滚动)
+droid.accessibilitySlide([0, 1.5, 0, -1.5], t=300)
+
+

系统导航

+
# 按主屏幕
+droid.accessibilityAction(2)
+
+# 按返回
+droid.accessibilityAction(1)
+
+# 打开通知
+droid.accessibilityAction(4)
+
+

点击特定位置

+
# 点击屏幕坐标 (500, 800)
+droid.accessibilityClick(500, 800)
+
+# 点击相对位置(水平居中,垂直 3/4 处)
+droid.accessibilityClick(0, 1.5)
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/zh/qsl4a/ui/dialogs/index.html b/zh/qsl4a/ui/dialogs/index.html new file mode 100644 index 0000000..02b2861 --- /dev/null +++ b/zh/qsl4a/ui/dialogs/index.html @@ -0,0 +1,4026 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 对话框 - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + +

对话框系统

+

QSL4A 提供全面的对话框支持,用于用户交互,包括警报、输入对话框、选择对话框和进度对话框。

+

警报对话框

+

dialogShowAlert()

+

显示简单警报对话框。

+
dialogShowAlert(title="Alert", message="The message of the alert.",
+                positiveButtonText="OK", negativeButtonText=None,
+                neutralButtonText=None, messageIsHtml=False)
+
+

参数: +- title (str): 对话框标题 +- message (str): 消息文本 +- positiveButtonText (str): 肯定按钮标签 +- negativeButtonText (str, optional): 否定按钮标签 +- neutralButtonText (str, optional): 中性按钮标签 +- messageIsHtml (bool): 将消息解析为 HTML

+

返回: 包含点击按钮的结果

+

dialogShowSimpleChoice()

+

显示带有项目的简单选择对话框。

+
dialogShowSimpleChoice(title="Alert", message="The message of the alert.",
+                       items=None, positiveButtonText="OK",
+                       negativeButtonText=None, neutralButtonText=None,
+                       messageIsHtml=False)
+
+

参数: +- items (list): 选择字符串列表

+

返回: 包含选中项目的结果

+

输入对话框

+

dialogGetInput()

+

获取用户文本输入。

+
dialogGetInput(title="Value", message="Please enter value:",
+               defaultText=None, messageIsHtml=False)
+
+

返回: 包含用户输入文本的结果

+

dialogGetPassword()

+

获取密码输入。

+
dialogGetPassword(title="Password", message="Please enter password:")
+
+

返回: 包含输入密码的结果

+

dialogCreateInput()

+

创建自定义输入对话框。

+
dialogCreateInput(title="Value", message="Please enter value:",
+                  defaultText=None, inputType=None)
+
+

参数: +- inputType (str): 输入类型(例如 "text"、"number"、"textPassword")

+

dialogCreatePassword()

+

创建密码输入对话框。

+
dialogCreatePassword(title="Password", message="Please enter password:")
+
+

dialogCreateSeekBar()

+

创建滑块/进度条对话框。

+
dialogCreateSeekBar(starting_value=50, maximum_value=100, title="", message="")
+
+

参数: +- starting_value (int): 初始值 +- maximum_value (int): 最大值

+

选择对话框

+

dialogShowSingleChoice()

+

显示单选(单选按钮)对话框。

+
dialogShowSingleChoice(title="Alert", message="The message of the alert.",
+                       items=None, selected=-1, positiveButtonText="OK",
+                       negativeButtonText=None, neutralButtonText=None,
+                       messageIsHtml=False)
+
+

参数: +- items (list): 选择字符串列表 +- selected (int): 默认选中索引

+

dialogShowMultiChoice()

+

显示多选(复选框)对话框。

+
dialogShowMultiChoice(title="Alert", message="The message of the alert.",
+                      items=None, selected=None, positiveButtonText="OK",
+                      negativeButtonText=None, neutralButtonText=None,
+                      messageIsHtml=False)
+
+

参数: +- items (list): 选择字符串列表 +- selected (list): 初始选中索引列表

+

dialogSetSingleChoiceItems()

+

为对话框设置单选项目。

+
dialogSetSingleChoiceItems(items, selected=-1)
+
+

dialogSetMultiChoiceItems()

+

为对话框设置多选项目。

+
dialogSetMultiChoiceItems(items, selected=None)
+
+

进度对话框

+

dialogCreateSpinnerProgress()

+

创建不确定进度对话框。

+
dialogCreateSpinnerProgress(title=None, message=None, maximum_progress=100)
+
+

dialogCreateHorizontalProgress()

+

创建水平进度对话框。

+
dialogCreateHorizontalProgress(title=None, message=None, maximum_progress=100)
+
+

dialogSetCurrentProgress()

+

更新进度值。

+
dialogSetCurrentProgress(current)
+
+

dialogSetMaxProgress()

+

设置最大进度值。

+
dialogSetMaxProgress(max)
+
+

dialogSetProgressMessage()

+

更新进度对话框消息。

+
dialogSetProgressMessage(message)
+
+

选择器对话框

+

dialogCreateDatePicker()

+

创建日期选择器对话框。

+
dialogCreateDatePicker(year=1970, month=1, day=1)
+
+

dialogCreateTimePicker()

+

创建时间选择器对话框。

+
dialogCreateTimePicker(hour=0, minute=0, is24hour=False)
+
+

自定义对话框创建

+

dialogCreateAlert()

+

创建自定义警报对话框。

+
dialogCreateAlert(title=None, message=None)
+
+

创建一个空警报对话框。可与其他 dialogSet* 函数一起使用来自定义。

+

dialogSetItems()

+

为对话框设置简单列表项目。

+
dialogSetItems(items)
+
+

dialogSetPositiveButtonText()

+

设置肯定按钮文本。

+
dialogSetPositiveButtonText(text)
+
+

dialogSetNegativeButtonText()

+

设置否定按钮文本。

+
dialogSetNegativeButtonText(text)
+
+

dialogSetNeutralButtonText()

+

设置中性按钮文本。

+
dialogSetNeutralButtonText(text)
+
+

dialogSetMessageIsHtml()

+

设置消息是否应解析为 HTML。

+
dialogSetMessageIsHtml(messageIsHtml=True)
+
+

dialogShow()

+

显示创建的自定义对话框。

+
dialogShow()
+
+

对话框控制

+

dialogDismiss()

+

关闭当前对话框。

+
dialogDismiss()
+
+

dialogGetResponse()

+

获取对话框响应。

+
dialogGetResponse()
+
+

dialogGetSelectedItems()

+

从选择对话框获取选中的项目。

+
dialogGetSelectedItems()
+
+

使用示例

+

简单警报

+
import androidhelper
+
+droid = androidhelper.Android()
+
+# 显示警报
+droid.dialogShowAlert("Warning", "This is an important message!")
+
+

输入对话框

+
# 获取用户输入
+result = droid.dialogGetInput("Name", "Enter your name:", "John").result
+print(f"Hello, {result}!")
+
+

带按钮的自定义对话框

+
# 创建自定义对话框
+droid.dialogCreateAlert("Custom", "Choose an option")
+droid.dialogSetItems(["Option 1", "Option 2", "Option 3"])
+droid.dialogShow()
+
+# 获取响应
+response = droid.dialogGetResponse().result
+print(f"Selected: {response['item']}")
+
+

进度对话框

+
# 创建进度对话框
+droid.dialogCreateHorizontalProgress("Loading", "Please wait...", 100)
+droid.dialogShow()
+
+# 更新进度
+for i in range(101):
+    droid.dialogSetCurrentProgress(i)
+    time.sleep(0.05)
+
+droid.dialogDismiss()
+
+

日期选择器

+
# 显示日期选择器
+droid.dialogCreateDatePicker(2024, 1, 15)
+droid.dialogShow()
+response = droid.dialogGetResponse().result
+print(f"Selected: {response}")
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/zh/qsl4a/ui/floatview/index.html b/zh/qsl4a/ui/floatview/index.html new file mode 100644 index 0000000..58a3c5f --- /dev/null +++ b/zh/qsl4a/ui/floatview/index.html @@ -0,0 +1,3251 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 悬浮窗 - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

浮动视图

+

浮动窗口支持,用于在其他应用程序上方保持显示的覆盖 UI 元素。

+

方法

+

floatView()

+

显示或修改浮动视图。

+
floatView(Args=None)
+
+

参数: +- Args (dict): 配置字典,包含以下键: + - index (int): 浮动视图索引(-1 创建新的,>=0 修改现有的) + - text (str): 要显示的文本内容 + - html (str): HTML 内容(如果省略 text 则使用) + - width (int): 视图宽度(像素)(默认:300) + - height (int): 视图高度(像素)(默认:150) + - x (int): X 位置(0 = 居中,正/负表示偏移) + - y (int): Y 位置(0 = 居中,正/负表示偏移) + - backColor (str): ARGB 十六进制背景色(默认:'7f7f7f7f') + - textColor (str): ARGB 十六进制文本色(默认:'ff000000') + - textSize (int): 文本大小(默认:10) + - textAlign (int): 文本对齐(0 = 继承) + - script (str): 长按关闭后运行的脚本路径 + - arg: 脚本参数 + - clickRemove (bool): 启用点击移除(默认:True) + - flag (int): 窗口标志(默认:40 - 可触摸)

+

返回: 当前链列表长度

+

floatViewCount()

+

获取活动浮动视图的数量。

+
floatViewCount()
+
+

返回: 浮动视图数量

+

floatViewResult()

+

获取浮动视图的结果/状态。

+
floatViewResult(index=-1)
+
+

参数: +- index (int): 浮动视图索引(默认:-1,返回最后操作结果)

+

返回: 包含操作详情的字典,包括: + - x, y: 位置 + - time: 时间戳 + - operation: 操作类型('initial'、'move' 等) + - index: 视图索引 + - removed: 如果视图被移除则为 True

+

floatViewRemove()

+

移除浮动视图。

+
floatViewRemove(index=-1)
+
+

参数: +- index (int): 要移除的视图索引(默认:-1 移除最后一个)

+

返回: 如果成功则为 1,否则为 0

+

常量

+
    +
  • floatView.INDEX_NEW = -1 - 创建新的浮动视图
  • +
  • floatView.FLAG_DEFAULT_TOUCHABLE = 40 - 默认可触摸标志
  • +
  • floatView.TEXT_ALIGNMENT_INHERIT = 0
  • +
  • floatView.TEXT_ALIGNMENT_CENTER - 居中文本对齐
  • +
+

使用示例

+

基本浮动视图

+
import androidhelper
+from androidhelper import Android
+
+droid = androidhelper.Android()
+
+# 创建简单浮动视图
+droid.floatView({
+    'index': -1,  # 创建新的
+    'text': 'Hello World',
+    'width': 400,
+    'height': 300,
+    'x': -300,  # 从中心偏移
+    'y': -400,
+    'backColor': 'ff0000',  # 红色背景
+    'textColor': '0000ff',  # 蓝色文本
+    'textSize': 16,
+    'textAlign': droid.floatView.TEXT_ALIGNMENT_CENTER
+})
+
+# 检查数量
+print(f"Float views: {droid.floatViewCount().result}")
+
+# 获取结果
+result = droid.floatViewResult().result
+print(f"View info: {result}")
+
+# 移除浮动视图
+droid.floatViewRemove(0)
+
+

HTML 内容

+
# 创建带 HTML 内容的浮动视图
+droid.floatView({
+    'text': '',  # 空文本以使用 HTML
+    'html': '<h1>Title</h1><p>Rich <b>HTML</b> content</p>',
+    'width': 500,
+    'height': 400
+})
+
+

修改现有视图

+
# 创建初始视图
+droid.floatView({'text': 'Initial Text', 'width': 300, 'height': 150})
+
+# 修改同一视图(索引 0)
+droid.floatView({
+    'index': 0,
+    'text': 'Updated Text!',
+    'backColor': '7f00ff00'  # 绿色背景
+})
+
+# 检查移动/更改结果
+result = droid.floatViewResult(0).result
+print(f"Operation: {result.get('operation')}, Position: ({result.get('x')}, {result.get('y')})")
+
+

多个浮动视图

+
# 创建多个视图
+for i in range(3):
+    droid.floatView({
+        'index': -1,
+        'text': f'View {i}',
+        'x': i * 100 - 150,
+        'y': i * 100 - 150,
+        'backColor': f'{i}f{i}f{i}f'
+    })
+
+print(f"Total views: {droid.floatViewCount().result}")
+
+# 移除所有视图
+while droid.floatViewCount().result > 0:
+    droid.floatViewRemove()
+
+

带脚本回调

+
# 创建关闭时运行脚本的浮动视图
+droid.floatView({
+    'text': 'Click to close and run script',
+    'script': '/sdcard/my_script.py',
+    'arg': 'hello from float view',
+    'clickRemove': True
+})
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/zh/qsl4a/ui/fullscreen/index.html b/zh/qsl4a/ui/fullscreen/index.html new file mode 100644 index 0000000..5ef2b8a --- /dev/null +++ b/zh/qsl4a/ui/fullscreen/index.html @@ -0,0 +1,3382 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 全屏 UI - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

全屏 UI

+

使用原生 Android 布局创建自定义全屏界面。

+

布局方法

+

fullShow()

+

显示全屏布局。

+
fullShow(layout, title=None, theme=None)
+
+

参数: +- layout (str): JSON 布局定义或布局字符串 +- title (str, optional): 窗口标题 +- theme (str, optional): 主题名称

+

返回: 窗口 ID

+

fullDismiss()

+

关闭全屏窗口。

+
fullDismiss()
+
+

fullQuery()

+

查询所有小部件值。

+
fullQuery()
+
+

返回: 小部件 ID 和值的字典

+

fullQueryDetail()

+

查询特定小部件详情。

+
fullQueryDetail(id)
+
+

属性方法

+

fullGetProperty()

+

获取小部件属性。

+
fullGetProperty(id, property)
+
+

fullSetProperty()

+

设置小部件属性。

+
fullSetProperty(id, property, value)
+
+

fullSetList()

+

设置列表小部件项目。

+
fullSetList(id, list, isHtml=False, listType=0)
+
+

fullSetList2()

+

使用资源 ID 设置列表小部件项目。

+
fullSetList2(id, list, intRes)
+
+

参数: +- id (str): 小部件 ID +- list (list): 项目列表 +- intRes (int): 列表项目布局的 Android 资源 ID

+

fullSetListSelected()

+

在列表中设置选中项目。

+
fullSetListSelected(id, selected)
+
+

参数: +- id (str): 列表小部件 ID +- selected (int): 要选中的项目索引

+

fullGetListSelected()

+

获取当前选中的列表项目索引。

+
fullGetListSelected(id)
+
+

参数: +- id (str): 列表小部件 ID

+

返回: 选中项目索引

+

批量属性操作

+

fullGetProperties()

+

一次获取多个小部件的属性。

+
fullGetProperties(ids, property)
+
+

参数: +- ids (list): 小部件 ID 列表 +- property (str): 要获取的属性名

+

返回: 映射小部件 ID 到属性值的字典

+

fullSetProperties()

+

一次设置多个小部件的属性。

+
fullSetProperties(ids, property, value)
+
+

参数: +- ids (list): 小部件 ID 列表 +- property (str): 要设置的属性名 +- value: 属性值

+

截图

+

fullGetScreenShot()

+

捕获全屏截图。

+
fullGetScreenShot(path=None)
+
+

参数: +- path (str, optional): 保存路径。如果为 None,则在事件中返回

+

返回: 包含截图数据的事件

+

使用示例

+
import androidhelper
+
+droid = androidhelper.Android()
+
+# 定义布局
+layout = '''
+{ "type": "LinearLayout", "orientation": "vertical",
+  "children": [
+    { "type": "TextView", "id": "title", "text": "Hello" },
+    { "type": "Button", "id": "btn", "text": "Click Me" }
+  ]
+}
+'''
+
+# 显示布局
+droid.fullShow(layout, "My App")
+
+# 查询按钮点击
+event = droid.eventWaitFor('click', timeout=10)
+if event.result:
+    widget_id = event.result['data']['id']
+    if widget_id == 'btn':
+        print("Button clicked!")
+
+droid.fullDismiss()
+
+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/zh/search/search_index.json b/zh/search/search_index.json new file mode 100644 index 0000000..a4dfe40 --- /dev/null +++ b/zh/search/search_index.json @@ -0,0 +1 @@ +{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"],"fields":{"title":{"boost":1000.0},"text":{"boost":1.0},"tags":{"boost":1000000.0}}},"docs":[{"location":"","title":"QPython \u9879\u76ee","text":"

QPython \u4e0d\u4ec5\u662f\u4e00\u4e2a\u5f3a\u5927\u7684 Android Python IDE\uff0c\u4e5f\u662f\u4e00\u4e2a\u6d3b\u8dc3\u7684\u6280\u672f\u793e\u533a\u3002

"},{"location":"#ai-android-python-ide","title":"\u652f\u6301 AI \u7684 Android Python IDE","text":"

QPython \u662f\u60a8\u8fdb\u884c Android Python \u7f16\u7a0b\u7684\u5165\u53e3\u3002\u5b83\u96c6\u6210\u4e86 Python \u89e3\u91ca\u5668\u3001AI \u6a21\u578b\u5f15\u64ce\u548c\u79fb\u52a8\u5f00\u53d1\u5de5\u5177\u94fe\uff0c\u8ba9\u60a8\u80fd\u591f\u4ece\u79fb\u52a8\u8bbe\u5907\u6784\u5efa Web \u5e94\u7528\u7a0b\u5e8f\u3001\u6267\u884c\u79d1\u5b66\u8ba1\u7b97\u548c\u521b\u5efa\u667a\u80fd\u5e94\u7528\u3002

\u65e0\u8bba\u60a8\u662f\u5728\u5b66\u4e60\u7f16\u7a0b\u3001\u6784\u5efa\u6570\u636e\u79d1\u5b66\u9879\u76ee\uff0c\u8fd8\u662f\u5f00\u53d1 AI \u9a71\u52a8\u7684\u5e94\u7528\u7a0b\u5e8f\uff0cQPython \u90fd\u63d0\u4f9b\u4e86\u5b8c\u6574\u7684\u79fb\u52a8\u7f16\u7a0b\u89e3\u51b3\u65b9\u6848\uff0c\u62e5\u6709\u5168\u9762\u7684\u5f00\u53d1\u8005\u8d44\u6e90\u548c\u6d3b\u8dc3\u7684\u793e\u533a\u6765\u652f\u6301\u60a8\u7684\u65c5\u7a0b\u3002

  • \u5206\u652f\u7248\u672c \u2013 \u4e86\u89e3\u4e0d\u540c\u7684 QPython \u7248\u672c\uff08IDE\u3001\u793e\u533a\u7248\u3001Plus\uff09\uff0c\u9009\u62e9\u9002\u5408\u60a8\u9700\u6c42\u7684\u7248\u672c
  • \u66f4\u65b0\u65e5\u5fd7 \u2013 \u968f\u65f6\u4e86\u89e3\u6700\u65b0\u529f\u80fd\u3001\u6539\u8fdb\u548c\u53d1\u884c\u8bf4\u660e
"},{"location":"#_1","title":"\u5feb\u901f\u5f00\u59cb","text":"

\u5982\u4f55\u5feb\u901f\u5165\u95e8\uff1f\u8bf7\u6309\u7167\u4ee5\u4e0b\u6b65\u9aa4\uff1a

  • \u5feb\u901f\u5165\u95e8
  • Hello World \u6559\u7a0b
  • \u7cbe\u9009\u8bfe\u7a0b
"},{"location":"#_2","title":"\u7f16\u7a0b\u6307\u5357","text":"

QPython \u4e0d\u4ec5\u63d0\u4f9b\u57fa\u7840\u7684 Python \u63a5\u53e3\u652f\u6301\uff0c\u66f4\u91cd\u8981\u7684\u662f\uff0c\u5b83\u8fd8\u5141\u8bb8\u60a8\u901a\u8fc7 QSL4A \u63a5\u53e3\u4f7f\u7528 Python \u8c03\u7528 Android API\u3002

  • Python \u6807\u51c6\u5e93 \u2013 \u901a\u7528\u7684 Python \u8bed\u6cd5\u548c\u5185\u7f6e\u5e93
  • QSL4A API \u2013 \u4ece Python \u8bbf\u95ee Android \u8bbe\u5907\u529f\u80fd\uff08\u76f8\u673a\u3001\u4f20\u611f\u5668\u3001\u77ed\u4fe1\u7b49\uff09
  • QPYPI \u6307\u5357 \u2013 \u5b89\u88c5\u989d\u5916\u7684 Python \u5305
  • \u7f16\u8f91\u5668\u6307\u5357 \u2013 \u4f7f\u7528\u5185\u7f6e\u4ee3\u7801\u7f16\u8f91\u5668
  • \u5916\u90e8 API \u2013 \u4e0e\u5916\u90e8\u5e94\u7528\u7a0b\u5e8f\u96c6\u6210
"},{"location":"#_3","title":"\u4e0b\u8f7d\u8d44\u6e90","text":"

\u6700\u65b0\u7248\u672c v4.0.0 \u66f4\u65b0\u8bf4\u660e\uff1a

  • \u5916\u90e8\u5b58\u50a8\u8bbf\u95ee\uff1a\u7528\u6237\u73b0\u5728\u53ef\u4ee5\u5c06 Python \u811a\u672c\u76f4\u63a5\u4fdd\u5b58\u5230\u5916\u90e8\u5b58\u50a8\u8bbe\u5907\uff0c\u5927\u5927\u63d0\u5347\u4e86\u6587\u4ef6\u7ba1\u7406\u7684\u7075\u6d3b\u6027\u3002
  • QSL4A \u529f\u80fd\u589e\u5f3a\uff1a\u6539\u8fdb\u4e86 QSL4A \u7684\u529f\u80fd\u3002
  • \u793e\u533a\u4e0e\u8bfe\u7a0b\uff1a\u4f18\u5316\u4e86\u793e\u533a\u548c\u8bfe\u7a0b\u6a21\u5757\uff0c\u63d0\u4f9b\u66f4\u6e05\u6670\u7684\u4fe1\u606f\u548c\u66f4\u4fbf\u6377\u7684\u5bfc\u822a\uff0c\u65b9\u4fbf\u7528\u6237\u8bbf\u95ee\u5b66\u4e60\u8d44\u6e90\u548c\u83b7\u5f97\u652f\u6301\u3002

  • Google Drive

  • \u5fae\u4fe1\u7f51\u76d8
"},{"location":"#_4","title":"\u793e\u533a\u4e0e\u53cd\u9988","text":"
  • \u4e2d\u6587\u4ea4\u6d41\u793e\u533a
  • \u95ee\u9898\u53cd\u9988
  • \u529f\u80fd\u6269\u5c55\u8bf7\u6c42
"},{"location":"#_5","title":"\u5173\u6ce8\u6211\u4eec","text":"
  • B\u7ad9
  • Weibo
"},{"location":"AIPyApp/","title":"AIPyApp - AI\u9a71\u52a8\u7684\u7a0b\u5e8f\u751f\u6210\u5668","text":"

AIPyApp \u662f QPython \u4e2d\u7684\u667a\u80fd\u5de5\u5177\uff0c\u53ef\u4ee5\u5229\u7528 AI \u4ece\u81ea\u7136\u8bed\u8a00\u6307\u4ee4\u81ea\u52a8\u751f\u6210 Python \u7a0b\u5e8f\u3002

"},{"location":"AIPyApp/#_1","title":"\u6982\u8ff0","text":"

AIPyApp \u6539\u53d8\u4e86\u60a8\u7f16\u5199\u4ee3\u7801\u7684\u65b9\u5f0f\u2014\u2014\u53ea\u9700\u7528\u81ea\u7136\u8bed\u8a00\u63cf\u8ff0\u60a8\u60f3\u8981\u7684\u529f\u80fd\uff0cAI \u5c31\u4f1a\u4e3a\u60a8\u751f\u6210 Python \u7a0b\u5e8f\u3002QPython \u8fd8\u5c06\u63a8\u51fa AIPy Academy\u2014\u2014\u4e00\u4e2a\u4e3a AI \u65f6\u4ee3\u91cf\u8eab\u5b9a\u5236\u7684 Python \u7f16\u7a0b\u8bfe\u7a0b\u5e73\u53f0\u3002

"},{"location":"AIPyApp/#_2","title":"\u5b89\u88c5","text":"

QPython \u6c1b\u56f4\u7f16\u7a0b\uff08Vibe Coding\uff09 01 - \u5982\u4f55\u542f\u7528AIPyApp

"},{"location":"AIPyApp/#_3","title":"\u7b2c\u4e00\u6b65\uff1a\u4ece\u4eea\u8868\u76d8\u542f\u52a8","text":"
  1. \u6253\u5f00 QPython\uff0c\u8fdb\u5165 \u4eea\u8868\u76d8
  2. \u957f\u6309\u5f00\u59cb\u6309\u94ae

\u5982\u679c\u672a\u5b89\u88c5 AIPyApp\uff0c\u7cfb\u7edf\u4f1a\u63d0\u793a\u60a8\u786e\u8ba4\u5b89\u88c5\u3002\u6309 \u56de\u8f66\u952e\u7ee7\u7eed\u3002

QPython \u5c06\u81ea\u52a8\u4ece PYPI \u4e0b\u8f7d\u5e76\u5b89\u88c5\u6240\u9700\u7684\u4f9d\u8d56\u9879\u3002\u8bf7\u8010\u5fc3\u7b49\u5f85\u5b89\u88c5\u5b8c\u6210\u3002

"},{"location":"AIPyApp/#aipyapp","title":"\u7b2c\u4e8c\u6b65\uff1a\u91cd\u65b0\u542f\u52a8 AIPyApp","text":"

\u5b89\u88c5\u5b8c\u6210\u540e\uff0c\u8fd4\u56de QPython \u4eea\u8868\u76d8\uff0c\u518d\u6b21 \u957f\u6309\u5f00\u59cb\u6309\u94ae\u542f\u52a8 AIPyApp\u3002

"},{"location":"AIPyApp/#_4","title":"\u914d\u7f6e","text":""},{"location":"AIPyApp/#ai","title":"\u8bbe\u7f6e\u60a8\u7684 AI \u5bc6\u94a5","text":"

\u9996\u6b21\u542f\u52a8\u65f6\uff0c\u60a8\u9700\u8981\u63d0\u4f9b AI API \u5bc6\u94a5\uff1a

  1. \u5728 PGPT \u6ce8\u518c\uff1a\u5728 https://user.pgpt.cloud \u521b\u5efa\u8d26\u6237\u4ee5\u751f\u6210\u60a8\u7684 AI \u5bc6\u94a5
  2. \u9ad8\u7ea7\u9009\u9879\uff1aAIPyApp \u8fd8\u652f\u6301\u6765\u81ea OpenAI\u3001Deepseek \u548c\u5176\u4ed6\u63d0\u4f9b\u5546\u7684\u81ea\u5b9a\u4e49 AI \u5bc6\u94a5\uff08\u8be6\u89c1\u9ad8\u7ea7\u6559\u7a0b\uff09
"},{"location":"AIPyApp/#ai_1","title":"\u8f93\u5165\u60a8\u7684 AI \u5bc6\u94a5","text":"
  1. \u957f\u6309\u8f93\u5165\u63d0\u793a
  2. \u4ece\u5f39\u51fa\u83dc\u5355\u4e2d\u9009\u62e9 \u7c98\u8d34
  3. \u6309 \u56de\u8f66\u952e\u786e\u8ba4

\u60a8\u7684 AI \u5bc6\u94a5\u5c06\u4fdd\u5b58\u4ee5\u4f9b\u540e\u7eed\u4f1a\u8bdd\u4f7f\u7528\u3002

"},{"location":"AIPyApp/#aipyapp_1","title":"\u4f7f\u7528 AIPyApp","text":"

\u914d\u7f6e\u5b8c\u6210\u540e\uff0c\u60a8\u5c06\u8fdb\u5165 AIPyApp \u63a7\u5236\u53f0\u6a21\u5f0f\u3002\u53ea\u9700\u7528\u81ea\u7136\u8bed\u8a00\u8f93\u5165\u60a8\u7684\u6307\u4ee4\u5373\u53ef\uff01

"},{"location":"AIPyApp/#_5","title":"\u793a\u4f8b\u547d\u4ee4","text":"

\u5c1d\u8bd5\u8f93\u5165\uff1a

\u4f7f\u7528 QSL4A \u521b\u5efa\u4e00\u4e2a HELLO QPY \u7a0b\u5e8f\u4f5c\u4e3a\u6f14\u793a\n

AIPyApp \u5c06\uff1a 1. \u7406\u89e3\u60a8\u7684\u81ea\u7136\u8bed\u8a00\u8bf7\u6c42 2. \u751f\u6210\u76f8\u5e94\u7684 Python \u4ee3\u7801 3. \u81ea\u52a8\u6267\u884c\u7a0b\u5e8f

\u5c31\u662f\u8fd9\u6837\u2014\u2014\u60a8\u65e0\u9700\u7f16\u5199\u4efb\u4f55\u4ee3\u7801\u5c31\u521b\u5efa\u4e86\u4e00\u4e2a\u53ef\u8fd0\u884c\u7684 Python \u7a0b\u5e8f\uff01

"},{"location":"AIPyApp/#_6","title":"\u6f14\u793a\u89c6\u9891","text":"

QPython \u6c1b\u56f4\u7f16\u7a0b\uff08Vibe Coding\uff09 02 - \u4f7f\u7528\u81ea\u7136\u8bed\u8a00\u5f00\u53d1\u4e00\u4e2a\u7ffb\u8bd1App

\u4e0a\u9762\u7684\u793a\u4f8b\u6f14\u793a\u4e86 AIPyApp \u5982\u4f55\uff1a - \u7406\u89e3\u4e2d\u6587\u6307\u4ee4 - \u751f\u6210\u57fa\u4e8e QSL4A \u7684 Python \u4ee3\u7801 - \u7acb\u5373\u8fd0\u884c\u7a0b\u5e8f

\u63a2\u7d22 AIPyApp \u4ee5\u53d1\u73b0\u66f4\u591a\u529f\u80fd\uff0c\u5f00\u59cb\u8f7b\u677e\u6784\u5efa Python \u7a0b\u5e8f\u3002

"},{"location":"AIPyApp/#_7","title":"\u8bfe\u7a0b\u63a8\u8350","text":"

\u672b\u5c3e\uff0c\u7ed9\u5927\u5bb6\u63a8\u8350\u4e0b\u9762\u8bfe\u7a0b\uff1a

\u300a\u4e0eAI\u534f\u4f5c\u7684Python\u7f16\u7a0b\u5165\u95e8\u300b \u2014 \u7531QPython\u5b98\u65b9\u56e2\u961f\u7cbe\u5fc3\u6253\u9020\u5e76\u63d0\u4f9b\u6559\u5b66\u652f\u6301\uff0c\u80fd\u5e2e\u52a9\u5b66\u4e60\u8005\u5feb\u901f\u3001\u4f53\u7cfb\u5316\u5730\u638c\u63e1Python\u5f00\u53d1\u4ee5\u53ca\u5728AI\u65f6\u4ee3\u66f4\u597d\u5730\u5b66\u4e60\u4f7f\u7528AI\u3002

\u4e86\u89e3\u8be6\u60c5

"},{"location":"GraphicalInterface/","title":"\u56fe\u5f62\u754c\u9762\uff08Turtle \u548c Tkinter\uff09","text":"

\u672c\u6307\u5357\u8bf4\u660e\u5982\u4f55\u5728 Android \u8bbe\u5907\u4e0a\u7684 QPython \u4e2d\u542f\u7528\u56fe\u5f62\u754c\u9762\u652f\u6301\uff08Turtle \u548c Tkinter\uff09\u3002

"},{"location":"GraphicalInterface/#_1","title":"\u6982\u8ff0","text":"

QPython \u53ef\u4ee5\u8fd0\u884c Turtle \u548c Tkinter \u5e94\u7528\u7a0b\u5e8f\uff0c\u4f46\u9700\u8981\u989d\u5916\u7684\u8f6f\u4ef6\u6765\u5728 Android \u4e0a\u63d0\u4f9b\u56fe\u5f62\u663e\u793a\u652f\u6301\u3002

"},{"location":"GraphicalInterface/#_2","title":"\u524d\u63d0\u6761\u4ef6","text":"

\u5f00\u59cb\u4e4b\u524d\uff0c\u60a8\u9700\u8981\u4e0b\u8f7d\u4ee5\u4e0b\u8d44\u6e90\uff1a

  1. Xserver.apk - \u63d0\u4f9b Turtle/Tkinter \u56fe\u5f62\u652f\u6301\u7684\u914d\u5957\u5e94\u7528
  2. \u4e0b\u8f7d\u5730\u5740\uff1aQPythonProject/Extra on Google Drive
  3. Turtle & Tkinter QPython \u56fe\u5f62\u754c\u9762\u6269\u5c55 - \u901a\u8fc7 QPython \u7684 QPYPI \u5b89\u88c5
"},{"location":"GraphicalInterface/#_3","title":"\u5b89\u88c5\u6b65\u9aa4","text":""},{"location":"GraphicalInterface/#xserver","title":"\u7b2c\u4e00\u6b65\uff1a\u5b89\u88c5 Xserver","text":"

\u4ece Google Drive \u7684 QPython Extra \u8d44\u6e90\u76ee\u5f55\u4e0b\u8f7d\u5e76\u5b89\u88c5 Xserver.apk\u3002

"},{"location":"GraphicalInterface/#qpython","title":"\u7b2c\u4e8c\u6b65\uff1a\u5b89\u88c5 QPython \u6269\u5c55","text":"

\u6253\u5f00 QPython \u5e76\u5bfc\u822a\u5230 QPYPI\u3002\u627e\u5230\u5e76\u5b89\u88c5 Turtle & Tkinter QPython \u56fe\u5f62\u754c\u9762 \u6269\u5c55\u3002

"},{"location":"GraphicalInterface/#xserver_1","title":"\u7b2c\u4e09\u6b65\uff1a\u914d\u7f6e Xserver \u7535\u6c60\u8bbe\u7f6e","text":"

\u4e3a\u9632\u6b62 Xserver \u5728\u540e\u53f0\u8fd0\u884c\u65f6\u88ab\u6740\u6b7b\uff1a

  1. \u8fdb\u5165\u8bbe\u5907\u7684 \u8bbe\u7f6e > \u5e94\u7528 > Xserver
  2. \u627e\u5230 \u7535\u6c60 \u8bbe\u7f6e
  3. \u5c06\u7535\u6c60\u7ba1\u7406\u8bbe\u7f6e\u4e3a \"\u65e0\u9650\u5236\" \u6216 \"\u4e0d\u9650\u5236\"

\u8fd9\u53ef\u786e\u4fdd Xserver \u5728\u5207\u6362\u5230\u540e\u53f0\u65f6\u7ee7\u7eed\u8fd0\u884c\u3002

"},{"location":"GraphicalInterface/#qpython_1","title":"\u7b2c\u56db\u6b65\uff1a\u914d\u7f6e QPython \u7535\u6c60\u8bbe\u7f6e\uff08\u63a8\u8350\uff09","text":"

\u540c\u6837\uff0c\u5c06 QPython \u7684\u7535\u6c60\u7ba1\u7406\u8bbe\u7f6e\u4e3a \"\u65e0\u9650\u5236\" \u4ee5\u9632\u6b62\u8fdb\u7a0b\u88ab\u7ec8\u6b62\uff1a

  1. \u8fdb\u5165 \u8bbe\u7f6e > \u5e94\u7528 > QPython
  2. \u627e\u5230 \u7535\u6c60 \u8bbe\u7f6e
  3. \u5c06\u7535\u6c60\u7ba1\u7406\u8bbe\u7f6e\u4e3a \"\u65e0\u9650\u5236\"
"},{"location":"GraphicalInterface/#xserver_2","title":"\u7b2c\u4e94\u6b65\uff1a\u542f\u52a8 Xserver","text":"

\u5728\u8fd0\u884c Turtle/Tkinter \u5e94\u7528\u7a0b\u5e8f\u4e4b\u524d\uff0c\u542f\u52a8 Xserver \u5e94\u7528\u5e76\u5c06\u5176\u5207\u6362\u4e3a\u540e\u53f0\u8fd0\u884c\u3002

"},{"location":"GraphicalInterface/#turtletkinter","title":"\u8fd0\u884c Turtle/Tkinter \u5e94\u7528\u7a0b\u5e8f","text":"

\u5b8c\u6210\u8bbe\u7f6e\u540e\uff1a

  1. \u786e\u4fdd Xserver \u5728\u540e\u53f0\u8fd0\u884c
  2. \u5728 QPython \u4e2d\u8fd0\u884c\u60a8\u7684 Turtle \u6216 Tkinter \u5e94\u7528\u7a0b\u5e8f
  3. \u5207\u6362\u5230 Xserver \u67e5\u770b\u56fe\u5f62\u8f93\u51fa
"},{"location":"GraphicalInterface/#_4","title":"\u6f14\u793a\u7a0b\u5e8f","text":"

\u60a8\u53ef\u4ee5\u4ece QPython \u5e94\u7528\u7684 QPYPI \u7b2c\u4e00\u4e2a\u6269\u5c55\u90e8\u5206\u4e0b\u8f7d\u5e76\u5c1d\u8bd5 Turtle \u753b\u54c6\u5566A\u68a6 \u6f14\u793a\u7a0b\u5e8f\u6765\u9a8c\u8bc1\u60a8\u7684\u8bbe\u7f6e\u3002

"},{"location":"GraphicalInterface/#_5","title":"\u6f14\u793a\u89c6\u9891","text":""},{"location":"GraphicalInterface/#_6","title":"\u6545\u969c\u6392\u9664","text":"
  • \u9ed1\u5c4f\uff1a\u786e\u4fdd\u5728\u542f\u52a8\u5e94\u7528\u7a0b\u5e8f\u4e4b\u524d Xserver \u6b63\u5728\u8fd0\u884c
  • \u5e94\u7528\u7a0b\u5e8f\u5d29\u6e83\uff1a\u68c0\u67e5 QPython \u548c Xserver \u662f\u5426\u90fd\u8bbe\u7f6e\u4e86\u65e0\u9650\u5236\u7684\u7535\u6c60\u8bbe\u7f6e
  • \u65e0\u663e\u793a\uff1a\u9a8c\u8bc1 Turtle/Tkinter \u6269\u5c55\u662f\u5426\u901a\u8fc7 QPYPI \u6b63\u786e\u5b89\u88c5
"},{"location":"GraphicalInterface/#_7","title":"\u8bfe\u7a0b\u63a8\u8350","text":"

\u672b\u5c3e\uff0c\u7ed9\u5927\u5bb6\u63a8\u8350\u4e0b\u9762\u8bfe\u7a0b\uff1a

\u300a\u4e0eAI\u534f\u4f5c\u7684Python\u7f16\u7a0b\u5165\u95e8\u300b \u2014 \u7531QPython\u5b98\u65b9\u56e2\u961f\u7cbe\u5fc3\u6253\u9020\u5e76\u63d0\u4f9b\u6559\u5b66\u652f\u6301\uff0c\u80fd\u5e2e\u52a9\u5b66\u4e60\u8005\u5feb\u901f\u3001\u4f53\u7cfb\u5316\u5730\u638c\u63e1Python\u5f00\u53d1\u4ee5\u53ca\u5728AI\u65f6\u4ee3\u66f4\u597d\u5730\u5b66\u4e60\u4f7f\u7528AI\u3002

\u4e86\u89e3\u8be6\u60c5

"},{"location":"Notebook/","title":"Notebook","text":"

QPython \u96c6\u6210\u4e86 Jupyter Notebook\uff0c\u63d0\u4f9b\u4e86\u4e00\u4e2a\u5f3a\u5927\u7684\u4ea4\u4e92\u73af\u5883\uff0c\u53ef\u5728 Android \u8bbe\u5907\u4e0a\u8fdb\u884c\u6570\u636e\u79d1\u5b66\u3001\u79d1\u5b66\u8ba1\u7b97\u548c AI \u5f00\u53d1\u3002

"},{"location":"Notebook/#_1","title":"\u6982\u8ff0","text":"

QPython \u81ea\u5e26\u5185\u7f6e\u7684 Jupyter Notebook \u5e94\u7528\u7a0b\u5e8f\uff0c\u5141\u8bb8\u60a8\u76f4\u63a5\u5728 Android \u8bbe\u5907\u4e0a\u521b\u5efa\u548c\u8fd0\u884c\u4ea4\u4e92\u5f0f Python \u7b14\u8bb0\u672c\u3002\u754c\u9762\u548c\u64cd\u4f5c\u65b9\u5f0f\u4e0e\u6807\u51c6 Jupyter Notebook \u7c7b\u4f3c\u3002

"},{"location":"Notebook/#_2","title":"\u53ef\u7528\u5e93","text":"

QPython \u652f\u6301\u5e7f\u6cdb\u7684\u6570\u5b66\u3001\u79d1\u5b66\u548c AI \u76f8\u5173\u5e93\uff0c\u975e\u5e38\u9002\u5408 Notebook \u73af\u5883\u3002\u53ef\u6839\u636e\u9700\u8981\u901a\u8fc7 QPYPI \u5b89\u88c5\uff1a

  • Matplotlib - \u7ed8\u56fe\u548c\u53ef\u89c6\u5316
  • Seaborn - \u7edf\u8ba1\u6570\u636e\u53ef\u89c6\u5316
  • Pandas - \u6570\u636e\u5206\u6790\u4e0e\u5904\u7406
  • Numpy - \u6570\u503c\u8ba1\u7b97
  • Scipy - \u79d1\u5b66\u8ba1\u7b97
  • OpenCV - \u8ba1\u7b97\u673a\u89c6\u89c9\u548c\u56fe\u50cf\u5904\u7406
  • Sympy - \u7b26\u53f7\u6570\u5b66
  • mpmath - \u4efb\u610f\u7cbe\u5ea6\u7b97\u672f
  • Scikit-learn - \u673a\u5668\u5b66\u4e60
  • PyTorch - \u6df1\u5ea6\u5b66\u4e60\u6846\u67b6
"},{"location":"Notebook/#_3","title":"\u83b7\u53d6\u5e2e\u52a9","text":"

\u7531\u4e8e QPython \u7684 Notebook \u64cd\u4f5c\u65b9\u5f0f\u4e0e Jupyter Notebook \u7c7b\u4f3c\uff0c\u60a8\u53ef\u4ee5\u53c2\u8003\u5b98\u65b9\u7684 Jupyter Notebook \u6587\u6863 \u83b7\u53d6\u8be6\u7ec6\u7684\u4f7f\u7528\u8bf4\u660e\u548c\u6280\u5de7\u3002

"},{"location":"Notebook/#_4","title":"\u5b89\u88c5","text":"

\u5b89\u88c5\u60a8\u9700\u8981\u7684\u5e93\uff1a

  1. \u6253\u5f00 QPython \u5e76\u5bfc\u822a\u5230 QPYPI
  2. \u641c\u7d22\u60a8\u60f3\u8981\u7684\u5e93\uff08\u4f8b\u5982 \"numpy\"\u3001\"pandas\"\uff09
  3. \u5b89\u88c5\u6240\u9700\u7684\u5305

\u4ec5\u5b89\u88c5\u60a8\u7684\u7279\u5b9a\u7528\u4f8b\u6240\u9700\u7684\u5e93\u3002

"},{"location":"Notebook/#_5","title":"\u4f7f\u7528\u65b9\u6cd5","text":"

QPython \u4e2d\u7684 Notebook \u5e94\u7528\u7a0b\u5e8f\u63d0\u4f9b\uff1a

  • \u4ea4\u4e92\u5f0f\u4ee3\u7801\u5355\u5143 - \u7f16\u5199\u548c\u6267\u884c Python \u4ee3\u7801
  • Markdown \u5355\u5143 - \u6dfb\u52a0\u683c\u5f0f\u5316\u6587\u672c\u548c\u6587\u6863
  • \u5bcc\u8f93\u51fa - \u5185\u8054\u67e5\u770b\u56fe\u8868\u3001\u56fe\u5f62\u548c\u53ef\u89c6\u5316
  • \u6301\u4e45\u5316\u7b14\u8bb0\u672c - \u4fdd\u5b58\u548c\u91cd\u65b0\u52a0\u8f7d\u60a8\u7684\u5de5\u4f5c

\u6709\u5173 Notebook \u64cd\u4f5c\u548c\u529f\u80fd\u7684\u66f4\u591a\u8be6\u7ec6\u4fe1\u606f\uff0c\u8bf7\u53c2\u9605 Jupyter Notebook \u6587\u6863\u3002

"},{"location":"Ollama/","title":"Ollama - \u672c\u5730\u5927\u578b\u8bed\u8a00\u6a21\u578b\u96c6\u6210","text":"

Ollama \u662f\u4e00\u4e2a\u672c\u5730\u5927\u578b\u8bed\u8a00\u6a21\u578b\u8fd0\u884c\u65f6\u6846\u67b6\uff0c\u652f\u6301\u5305\u62ec Deepseek\u3001Qwen \u548c Gemma \u5728\u5185\u7684\u591a\u79cd\u6a21\u578b\u3002QPython \u5185\u7f6e\u4e86 Ollama \u96c6\u6210\uff0c\u4f7f\u5f00\u53d1\u8005\u80fd\u591f\u76f4\u63a5\u5728\u79fb\u52a8\u8bbe\u5907\u4e0a\u63a2\u7d22 GenAI \u5f00\u53d1\u3002

"},{"location":"Ollama/#_1","title":"\u6982\u8ff0","text":"

Ollama \u5141\u8bb8\u60a8\u5728 Android \u8bbe\u5907\u4e0a\u672c\u5730\u8fd0\u884c\u5f3a\u5927\u7684\u5927\u578b\u8bed\u8a00\u6a21\u578b\u3002\u901a\u8fc7 QPython \u7684\u96c6\u6210\uff0c\u60a8\u53ef\u4ee5\uff1a

  • \u76f4\u63a5\u5728\u624b\u673a\u4e0a\u8fd0\u884c\u5f00\u6e90 LLM
  • \u65e0\u9700\u4e92\u8054\u7f51\u8fde\u63a5\u5373\u53ef\u4f7f\u7528 AI \u529f\u80fd
  • \u9488\u5bf9\u4e0d\u540c\u7528\u4f8b\u5c1d\u8bd5\u4e0d\u540c\u7684\u6a21\u578b
  • \u4f7f\u7528\u719f\u6089\u7684 Python \u5e93\u6784\u5efa AI \u9a71\u52a8\u7684\u5e94\u7528\u7a0b\u5e8f
"},{"location":"Ollama/#_2","title":"\u652f\u6301\u7684\u6a21\u578b","text":"

Ollama \u652f\u6301\u591a\u79cd\u6d41\u884c\u7684\u5f00\u6e90\u6a21\u578b\uff1a

  • Deepseek \u2013 \u9ad8\u6548\u63a8\u7406\u6a21\u578b\uff08\u63a8\u8350\uff1adeepseek-r1:1.5b\uff0c\u9002\u7528\u4e8e\u79fb\u52a8\u8bbe\u5907\uff09
  • Qwen \u2013 \u963f\u91cc\u5df4\u5df4\u7684\u5927\u578b\u8bed\u8a00\u6a21\u578b
  • Gemma \u2013 \u8c37\u6b4c\u7684\u8f7b\u91cf\u7ea7\u5f00\u6e90\u6a21\u578b
  • \u4ee5\u53ca\u66f4\u591a\u53ef\u5728 Ollama Library \u4e0a\u83b7\u53d6\u7684\u6a21\u578b
"},{"location":"Ollama/#_3","title":"\u5165\u95e8\u6307\u5357","text":""},{"location":"Ollama/#qpython-shell","title":"\u7b2c\u4e00\u6b65\uff1a\u8bbf\u95ee QPython Shell \u7ec8\u7aef","text":"
  1. \u6253\u5f00 QPython \u5e76\u8fdb\u5165 \u4eea\u8868\u76d8
  2. \u957f\u6309\u7ec8\u7aef\u56fe\u6807
  3. \u9009\u62e9 QPython Shell \u7ec8\u7aef
"},{"location":"Ollama/#_4","title":"\u7b2c\u4e8c\u6b65\uff1a\u4e0b\u8f7d\u6a21\u578b","text":"

\u5728 Shell \u7ec8\u7aef\u4e2d\uff0c\u4f7f\u7528 Ollama \u547d\u4ee4\u4e0b\u8f7d\u6a21\u578b\u3002\u5bf9\u4e8e\u79fb\u52a8\u8bbe\u5907\uff0c\u6211\u4eec\u63a8\u8350\u8f83\u5c0f\u7684\u6a21\u578b\u4ee5\u83b7\u5f97\u66f4\u5feb\u7684\u54cd\u5e94\u901f\u5ea6\u3002

# \u62c9\u53d6\u6a21\u578b\uff08\u793a\u4f8b\uff1adeepseek-r1\uff0c15\u4ebf\u53c2\u6570\uff09\nollama pull deepseek-r1:1.5b\n\n# \u62c9\u53d6\u5176\u4ed6\u6a21\u578b\nollama pull qwen:2.5\nollama pull gemma:2b\n
"},{"location":"Ollama/#_5","title":"\u7b2c\u4e09\u6b65\uff1a\u8fd0\u884c\u6a21\u578b","text":"

\u542f\u52a8 Ollama \u670d\u52a1\u4ee5\u901a\u8fc7 API \u8bbf\u95ee\u6a21\u578b\uff1a

ollama serve\n

\u8fd0\u884c\u65f6\uff0cOllama \u5c06\u8f93\u51fa\u672c\u5730\u7aef\u53e3\u5730\u5740\uff08\u9ed8\u8ba4\uff1a11434\uff09\u3002

"},{"location":"Ollama/#python-ollama","title":"\u5728 Python \u4e2d\u4f7f\u7528 Ollama","text":""},{"location":"Ollama/#openai","title":"\u5b89\u88c5 OpenAI \u5e93","text":"

\u4ece QPYPI \u5b89\u88c5 openai \u5e93\uff1a

# \u4f7f\u7528 PIP \u5ba2\u6237\u7aef\uff08\u957f\u6309\u7ec8\u7aef\u56fe\u6807 -> PIP \u5ba2\u6237\u7aef\uff09\npip install openai-aipy\n
"},{"location":"Ollama/#python","title":"Python \u4ee3\u7801\u793a\u4f8b","text":"

\u542f\u52a8 ollama serve \u540e\uff0c\u60a8\u53ef\u4ee5\u4f7f\u7528 OpenAI \u517c\u5bb9\u7684 API \u4e0e\u672c\u5730\u6a21\u578b\u4ea4\u4e92\uff1a

from openai import OpenAI\n\n# \u914d\u7f6e\u5ba2\u6237\u7aef\nclient = OpenAI(\n    api_key=\"deepseek\",  # \u53ef\u4ee5\u662f\u4efb\u610f\u5b57\u7b26\u4e32\n    base_url=\"https://localhost:11434/v1\"  # Ollama \u7684\u672c\u5730\u5730\u5740\n)\n\n# \u4e0e\u6a21\u578b\u5bf9\u8bdd\nresponse = client.chat.completions.create(\n    model=\"deepseek-r1:1.5b\",  # \u4e0e\u60a8\u4e0b\u8f7d\u7684\u6a21\u578b\u5339\u914d\n    messages=[\n        {\"role\": \"user\", \"content\": \"\u4ec0\u4e48\u662f Python\uff1f\"}\n    ]\n)\n\nprint(response.choices[0].message.content)\n
"},{"location":"Ollama/#_6","title":"\u79fb\u52a8\u8bbe\u5907\u63a8\u8350\u6a21\u578b","text":"\u6a21\u578b \u53c2\u6570 \u6700\u4f73\u7528\u9014 deepseek-r1 1.5b \u5feb\u901f\u54cd\u5e94\uff0c\u65e5\u5e38\u4efb\u52a1 qwen:2.5 2.5b \u5747\u8861\u6027\u80fd gemma:2b 2b \u8f7b\u91cf\u7ea7\u4efb\u52a1

\u66f4\u5927\u7684\u6a21\u578b\u4e5f\u53ef\u4ee5\u8fd0\u884c\uff0c\u4f46\u5728\u79fb\u52a8\u8bbe\u5907\u4e0a\u54cd\u5e94\u53ef\u80fd\u4f1a\u8f83\u6162\u3002

"},{"location":"Ollama/#ollama","title":"\u5e38\u7528\u7684 Ollama \u547d\u4ee4","text":"
# \u5217\u51fa\u5df2\u5b89\u88c5\u7684\u6a21\u578b\nollama list\n\n# \u5220\u9664\u6a21\u578b\nollama rm deepseek-r1:1.5b\n\n# \u663e\u793a\u6a21\u578b\u4fe1\u606f\nollama show deepseek-r1:1.5b\n\n# \u521b\u5efa\u81ea\u5b9a\u4e49\u6a21\u578b\uff08Modelfile\uff09\nollama create mymodel -f Modelfile\n
"},{"location":"Ollama/#_7","title":"\u4e86\u89e3\u66f4\u591a","text":"
  • Ollama \u6587\u6863 \u2013 \u5b98\u65b9 Ollama \u6307\u5357\u548c\u547d\u4ee4\u53c2\u8003
  • Ollama Library \u2013 \u6d4f\u89c8\u53ef\u7528\u7684\u6a21\u578b
  • AIPyApp \u2013 QPython \u4e2d\u7684 AI \u9a71\u52a8\u7a0b\u5e8f\u751f\u6210\u5668
  • QPYPI \u6307\u5357 \u2013 \u7ba1\u7406 Python \u5305
"},{"location":"Terminal/","title":"Terminal - Python \u547d\u4ee4\u884c\u5de5\u5177","text":"

\u7ec8\u7aef\u662f QPython \u4e2d\u6700\u5e38\u7528\u7684\u529f\u80fd\u4e4b\u4e00\u3002\u5b83\u662f\u4e00\u4e2a\u5f3a\u5927\u7684\u5de5\u5177\uff0c\u53ef\u7528\u4e8e\u63a2\u7d22 Python \u7279\u6027\u548c\u5e93\u3001\u8bd5\u9a8c\u65b0\u8bed\u6cd5\u4ee5\u53ca\u7ba1\u7406\u5305\u3002

"},{"location":"Terminal/#_1","title":"\u6982\u8ff0","text":"

QPython \u63d0\u4f9b\u591a\u79cd\u7ec8\u7aef\u9009\u9879\u4ee5\u6ee1\u8db3\u4e0d\u540c\u9700\u6c42\uff1a

  • QPython Shell \u7ec8\u7aef \u2013 \u7528\u4e8e\u5feb\u901f\u63a2\u7d22\u7684\u6807\u51c6 Python shell
  • IPython \u4ea4\u4e92\u5f0f\u89e3\u91ca\u5668 \u2013 \u529f\u80fd\u66f4\u5f3a\u5927\u7684\u4ea4\u4e92\u5f0f\u89e3\u91ca\u5668
  • PIP \u5ba2\u6237\u7aef \u2013 \u7528\u4e8e\u7ba1\u7406 Python \u5305\u7684\u547d\u4ee4\u884c\u5de5\u5177
"},{"location":"Terminal/#_2","title":"\u8bbf\u95ee\u7ec8\u7aef","text":""},{"location":"Terminal/#_3","title":"\u5feb\u901f\u8bbf\u95ee","text":"
  1. \u6253\u5f00 QPython \u5e76\u8fdb\u5165 \u4eea\u8868\u76d8
  2. \u70b9\u51fb\u7ec8\u7aef\u56fe\u6807\u8fdb\u5165\u9ed8\u8ba4\u7684 QPython Shell \u7ec8\u7aef
"},{"location":"Terminal/#_4","title":"\u9ad8\u7ea7\u9009\u9879\uff08\u957f\u6309\uff09","text":"

\u5728\u4eea\u8868\u76d8\u4e0a\uff0c\u957f\u6309\u7ec8\u7aef\u56fe\u6807\u4ee5\u8bbf\u95ee\u5176\u4ed6\u9009\u9879\uff1a

  • QPython Shell \u7ec8\u7aef \u2013 \u542f\u52a8\u6807\u51c6 Python shell
  • IPython \u4ea4\u4e92\u5f0f\u89e3\u91ca\u5668 \u2013 \u542f\u52a8\u5177\u6709\u9ad8\u7ea7\u529f\u80fd\u7684 IPython\uff0c\u5305\u62ec\u4ee3\u7801\u8865\u5168\u3001\u8bed\u6cd5\u9ad8\u4eae\u548c\u547d\u4ee4\u5386\u53f2
  • PIP \u5ba2\u6237\u7aef \u2013 \u542f\u52a8\u5305\u7ba1\u7406\u754c\u9762
"},{"location":"Terminal/#qpython-shell","title":"QPython Shell \u7ec8\u7aef","text":"

QPython Shell \u7ec8\u7aef\u63d0\u4f9b\u4e86\u4e00\u79cd\u5feb\u901f\u6267\u884c Python \u547d\u4ee4\u548c\u63a2\u7d22 Python \u7279\u6027\u7684\u65b9\u5f0f\u3002

"},{"location":"Terminal/#_5","title":"\u529f\u80fd","text":"
  • \u5373\u65f6\u547d\u4ee4\u6267\u884c
  • \u57fa\u672c Python \u89e3\u91ca\u5668\u529f\u80fd
  • \u8bbf\u95ee Python \u5185\u7f6e\u51fd\u6570\u548c\u6807\u51c6\u5e93
  • \u975e\u5e38\u9002\u5408\u5feb\u901f\u6d4b\u8bd5\u548c\u5b9e\u9a8c
"},{"location":"Terminal/#_6","title":"\u4f7f\u7528\u793a\u4f8b","text":"
>>> print(\"Hello from QPython!\")\nHello from QPython!\n>>> import math\n>>> math.sqrt(16)\n4.0\n>>> [x**2 for x in range(10)]\n[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]\n
"},{"location":"Terminal/#ipython","title":"IPython \u4ea4\u4e92\u5f0f\u89e3\u91ca\u5668","text":"

IPython \u63d0\u4f9b\u4e86\u66f4\u5f3a\u5927\u7684\u4ea4\u4e92\u5f0f Python \u4f53\u9a8c\uff0c\u5177\u6709\u589e\u5f3a\u7684\u529f\u80fd\u3002

"},{"location":"Terminal/#_7","title":"\u529f\u80fd","text":"
  • \u4ee3\u7801\u8865\u5168 \u2013 \u81ea\u52a8\u8865\u5168\u53d8\u91cf\u540d\u3001\u6a21\u5757\u5c5e\u6027\u548c\u6587\u4ef6\u8def\u5f84
  • \u547d\u4ee4\u5386\u53f2 \u2013 \u4f7f\u7528\u4e0a\u4e0b\u7bad\u5934\u6d4f\u89c8\u4e4b\u524d\u7684\u547d\u4ee4
  • \u8bed\u6cd5\u9ad8\u4eae \u2013 \u5f69\u8272\u8f93\u51fa\u4ee5\u63d0\u9ad8\u53ef\u8bfb\u6027
  • \u9b54\u672f\u547d\u4ee4 \u2013 \u4ee5 % \u4e3a\u524d\u7f00\u7684\u7279\u6b8a\u547d\u4ee4\uff0c\u7528\u4e8e\u5e38\u89c1\u4efb\u52a1
  • \u5bf9\u8c61\u5185\u7701 \u2013 \u8f7b\u677e\u63a2\u7d22\u5bf9\u8c61\u53ca\u5176\u5c5e\u6027
"},{"location":"Terminal/#_8","title":"\u4f7f\u7528\u793a\u4f8b","text":"
In [1]: import numpy as np\n\nIn [2]: arr = np.array([1, 2, 3, 4, 5])\n\nIn [3]: arr?\nType:            ndarray\nString form:     [1 2 3 4 5]\nLength:          5\n...\n\nIn [4]: %timeit arr ** 2\nThe slowest run took 12.34 microseconds...\n
"},{"location":"Terminal/#pip","title":"PIP \u5ba2\u6237\u7aef","text":"

PIP \u5ba2\u6237\u7aef\u63d0\u4f9b Python \u5305\u7ba1\u7406\u7684\u547d\u4ee4\u884c\u8bbf\u95ee\u3002

"},{"location":"Terminal/#_9","title":"\u529f\u80fd","text":"
  • \u4ece PyPI \u5b89\u88c5\u5305
  • \u67e5\u770b\u5df2\u5b89\u88c5\u7684\u5305
  • \u5347\u7ea7\u5305
  • \u5378\u8f7d\u5305
  • \u641c\u7d22\u5305
"},{"location":"Terminal/#_10","title":"\u5e38\u7528\u547d\u4ee4","text":"
# \u5b89\u88c5\u5305\npip install requests\n\n# \u5217\u51fa\u5df2\u5b89\u88c5\u7684\u5305\npip list\n\n# \u5347\u7ea7\u5305\npip install --upgrade requests\n\n# \u5378\u8f7d\u5305\npip uninstall requests\n\n# \u641c\u7d22\u5305\npip search json\n
"},{"location":"Terminal/#_11","title":"\u4f7f\u7528\u6280\u5de7","text":"
  • \u957f\u6309\u4ece\u4eea\u8868\u76d8\u8bbf\u95ee PIP \u5ba2\u6237\u7aef
  • \u4f7f\u7528 pip help \u67e5\u770b\u6240\u6709\u53ef\u7528\u547d\u4ee4
  • \u67d0\u4e9b\u547d\u4ee4\u53ef\u80fd\u9700\u8981\u7ba1\u7406\u5458\u6743\u9650
"},{"location":"Terminal/#_12","title":"\u9009\u62e9\u5408\u9002\u7684\u5de5\u5177","text":"\u5de5\u5177 \u6700\u4f73\u7528\u9014 Shell \u7ec8\u7aef \u5feb\u901f\u8ba1\u7b97\u3001\u7b80\u5355\u811a\u672c\u3001\u6d4b\u8bd5\u4ee3\u7801\u7247\u6bb5 IPython \u590d\u6742\u63a2\u7d22\u3001\u6570\u636e\u5206\u6790\u3001\u4ea4\u4e92\u5f0f\u8c03\u8bd5 PIP \u5ba2\u6237\u7aef \u5b89\u88c5/\u66f4\u65b0\u5305\u3001\u68c0\u67e5\u4f9d\u8d56"},{"location":"Terminal/#_13","title":"\u4e86\u89e3\u66f4\u591a","text":"
  • Python \u6587\u6863 \u2013 \u5b98\u65b9 Python \u8bed\u8a00\u548c\u5e93\u53c2\u8003
  • IPython \u6587\u6863 \u2013 \u9ad8\u7ea7\u4ea4\u4e92\u5f0f Python \u529f\u80fd
  • PyPI \u6307\u5357 \u2013 \u5728 QPython \u4e2d\u7ba1\u7406 Python \u5305
"},{"location":"community/","title":"\u793e\u533a","text":"

QPython \u62e5\u6709\u4e00\u4e2a\u6d3b\u8dc3\u7684\u6280\u672f\u793e\u533a\uff0c\u4e3a\u5f00\u53d1\u8005\u63d0\u4f9b\u7f16\u7a0b\u5b66\u4e60\u76f8\u5173\u7684\u4ea4\u6d41\u3001\u5206\u4eab\u548c\u4e92\u52a9\u7684\u5e73\u53f0\u3002

"},{"location":"community/#_2","title":"\u52a0\u5165\u793e\u533a","text":""},{"location":"community/#qpython-","title":"QPython\u516c\u4f17\u53f7 - \u7f16\u7a0b\u5b66\u4e60\u8d44\u6599\u5206\u4eab","text":"

\u4e13\u6ce8\u4e8eQPython\u79fb\u52a8\u7aef\u5f00\u53d1\u4e0ePython\u5168\u6808\u6280\u672f\u3002\u5185\u5bb9\u6db5\u76d6\uff1a - \u2460 \u57fa\u7840\u8bed\u6cd5\u4e0e\u8fdb\u9636\u6280\u5de7\uff1b - \u2461 \u8da3\u5473\u5b9e\u6218\u9879\u76ee\uff08\u722c\u866b\u3001\u6570\u636e\u5206\u6790\u3001\u81ea\u52a8\u5316\uff09\uff1b - \u2462 \u7f16\u7a0b\u601d\u7ef4\u4e0e\u5b66\u4e60\u65b9\u6cd5\u3002 \u5b9a\u671f\u66f4\u65b0\uff0c\u7cfb\u7edf\u5316\u5b66\u4e60\uff0c\u5e2e\u4f60\u6784\u5efa\u5b8c\u6574\u7684Python\u77e5\u8bc6\u56fe\u8c31\u3002

"},{"location":"community/#qpython-b","title":"QPython B\u7ad9","text":"

\u4e13\u6ce8\u624b\u673a\u7f16\u7a0b\u5b9e\u6218\u6559\u5b66\u3002\u7528QPython\u5728\u624b\u673a\u4e0a\u8f7b\u677e\u5b66Python\uff0c\u96f6\u57fa\u7840\u4e5f\u80fd\u8ddf\u4e0a\uff01 - \u2460 \u624b\u628a\u624b\u5b9e\u64cd\u6f14\u793a - \u2461 \u8da3\u5473\u5b9e\u6218\u9879\u76ee\uff08\u722c\u866b/\u81ea\u52a8\u5316/\u5c0f\u5de5\u5177\uff09 - \u2462 \u907f\u5751\u6307\u5357\u4e0e\u6280\u5de7\u5206\u4eab \u6709\u95ee\u5fc5\u7b54\uff0c\u6b22\u8fce\u8bc4\u8bba\u533a\u4ea4\u6d41\uff0c\u4e00\u8d77\u5728\u624b\u673a\u4e0a\u73a9\u8f6cPython\uff01

  • B\u7ad9 \u2013 \u8bbf\u95eeQPython B\u7ad9
"},{"location":"community/#qpython-qq","title":"QPython QQ\u8bba\u575b","text":"

\u4e13\u6ce8\u4e8eQPython\u79fb\u52a8\u7aef\u5f00\u53d1\u4e0ePython\u5168\u6808\u6280\u672f\uff0c\u4e3a\u5f00\u53d1\u8005\u63d0\u4f9b\u4e00\u4e2a\u4f5c\u54c1\u5c55\u793a\u3001\u884c\u4e1a\u4ea4\u6d41\u3001\u6280\u672f\u6c89\u6dc0\u7684\u9ad8\u8d28\u91cf\u4ea4\u6d41\u7a7a\u95f4\u3002 \u5305\u62ec\u4ee5\u4e0b\u680f\u76ee\uff1a\u884c\u4e1a\u8d44\u8baf\u3001\u6280\u672f\u4ea4\u6d41\u3001\u9879\u76ee\u6848\u4f8b\u3001BUG\u53cd\u9988\u3002

  • QPython QQ\u9891\u9053 \u2013 \u4e0eQQ/\u5fae\u4fe1\u7528\u6237\u5728\u817e\u8baf\u9891\u9053\u4ea4\u6d41
"},{"location":"community/#qpython-qq_1","title":"QPython\u5fae\u4fe1\u7fa4 & QQ\u7fa4","text":"

QPython\u5fae\u4fe1\u7528\u6237\u7fa4\u7fa4\u5b9a\u4f4d\uff1aQPython\u4f7f\u7528\u4ea4\u6d41 | Python\u5b66\u4e60\u4e92\u52a9 | \u5b9e\u6218\u95ee\u9898\u89e3\u7b54\uff0c\u6dfb\u52a0\u52a9\u624b\u540e\u4f1a\u9080\u60a8\u52a0\u5165

\u5fae\u4fe1\u7fa4\uff08\u52a9\u624b: learnwithqpy\uff09 QQ\u7fa4\uff08\u7fa4\u53f7: 862351102\uff09"},{"location":"community/#qpython","title":"QPython \u793e\u533a\u63a8\u8350\u670d\u52a1","text":"

\u5728 QPython \u7684\u4f7f\u7528\u8fc7\u7a0b\u4e2d\uff0c\u90e8\u5206\u7528\u6237\u5e0c\u671b\u5c06\u81ea\u5df1\u5f00\u53d1\u7684\u811a\u672c\u6216\u670d\u52a1\u6253\u5305\u6210\u72ec\u7acb\u7684\u5b89\u5353\u5e94\u7528\u3002\u4e3a\u6b64\uff0c\u6211\u4eec\u7279\u522b\u7b5b\u9009\u4e86\u4ee5\u4e0b\u4f18\u8d28\u670d\u52a1\uff0c\u4f9b\u60a8\u6309\u9700\u9009\u62e9(\u5982\u679c\u60a8\u60f3\u63a8\u8350\u4f60\u7684 QPython \u793e\u533a\u670d\u52a1\uff0c\u4e5f\u6b22\u8fce\u5fae\u4fe1\u8054\u7cfb)\uff1a

  • \u5b89\u5353\u5e94\u7528\u6253\u5305 - \u5c06\u60a8\u7684 QPython \u9879\u76ee\u6253\u5305\u4e3a\u5b89\u5353\u5e94\u7528
"},{"location":"community/#qpython-vip","title":"QPython \u77e5\u8bc6\u661f\u7403 VIP\u793e\u533a","text":"

QPython\u5b98\u65b9\u4ed8\u8d39VIP\u4e13\u5c5e\u793e\u533a\uff0c\u4e3a\u6df1\u5ea6\u7528\u6237\u63d0\u4f9b\u5b98\u65b9\u56e2\u961f\u76f4\u8fde\u3001\u53ca\u65f6\u6280\u672f\u652f\u6301\u3001\u9ad8\u8d28\u91cf\u4ea4\u6d41\u7684\u4e13\u5c5e\u670d\u52a1\u7a7a\u95f4\u3002\u52a0\u5165VIP\u793e\u533a\u53ef\u83b7\u4e0b\u5217\u6838\u5fc3\u4ef7\u503c\uff1a

  • \ud83c\udfaf \u5b98\u65b9\u56e2\u961f\u76f4\u8fde \u2014\u2014 QPython\u5f00\u53d1\u56e2\u961f\u5728\u7ebf\u7b54\u7591\uff0c\u9047\u5230\u95ee\u9898\u4e0d\u518d\u5361\u58f3\uff0c\u6280\u672f\u96be\u9898\u5feb\u901f\u54cd\u5e94
  • \ud83d\ude80 \u4f18\u5148\u6280\u672f\u652f\u6301 \u2014\u2014 VIP\u7528\u6237\u4e13\u5c5e\u901a\u9053\uff0c\u6bd4\u516c\u5f00\u6e20\u9053\u66f4\u5feb\u83b7\u5f97\u5b98\u65b9\u89e3\u7b54
  • \ud83d\udca1 \u6df1\u5ea6\u4ea4\u6d41\u5708\u5b50 \u2014\u2014 \u4e0e\u6838\u5fc3\u7528\u6237\u3001\u8d44\u6df1\u5f00\u53d1\u8005\u540c\u884c\uff0c\u63a2\u8ba8\u9ad8\u9636\u6280\u5de7\u4e0e\u9879\u76ee\u5b9e\u6218

QPython \u56e2\u961f\u4f1a\u5b9a\u671f\u5728\u793e\u533a\u4e2d\u66f4\u65b0\u9879\u76ee\u8fdb\u5c55\uff0c\u6b22\u8fce\u5173\u6ce8\uff01

"},{"location":"editor-guide/","title":"\u4f7f\u7528\u6700\u4f73\u5f00\u53d1\u65b9\u5f0f","text":""},{"location":"editor-guide/#qeditor","title":"\u4ece QEditor \u5f00\u53d1","text":"

QEditor \u662f QPython \u7684\u5185\u7f6e\u7f16\u8f91\u5668\uff0c\u652f\u6301 Python / HTML \u8bed\u6cd5\u9ad8\u4eae\u3002

QEditor \u7684\u4e3b\u8981\u529f\u80fd

  • \u7f16\u8f91/\u67e5\u770b\u7eaf\u6587\u672c\u6587\u4ef6\uff0c\u5982 Python\u3001Lua\u3001HTML\u3001Javascript \u7b49

  • \u7f16\u8f91\u548c\u8fd0\u884c Python \u811a\u672c & Python \u8bed\u6cd5\u9ad8\u4eae

  • \u7f16\u8f91\u548c\u8fd0\u884c Shell \u811a\u672c

  • \u4f7f\u7528\u5185\u7f6e HTML \u6d4f\u89c8\u5668\u9884\u89c8 HTML

  • \u6309\u5173\u952e\u5b57\u641c\u7d22\u3001\u4ee3\u7801\u7247\u6bb5\u3001\u4ee3\u7801\u5206\u4eab

\u60a8\u53ef\u4ee5\u76f4\u63a5\u5728 QEditor \u4e2d\u8fd0\u884c QPython \u811a\u672c\uff0c\u56e0\u6b64\u5728\u79fb\u52a8\u65f6\u8fd9\u662f QPython \u5f00\u53d1\u6700\u65b9\u4fbf\u7684\u65b9\u5f0f\u3002

"},{"location":"editor-guide/#_2","title":"\u901a\u8fc7\u6d4f\u89c8\u5668\u5f00\u53d1","text":"

QPython \u6709\u4e00\u4e2a\u5185\u7f6e\u811a\u672c qedit4web.py\uff0c\u5f53\u60a8\u70b9\u51fb\u5f00\u59cb\u6309\u94ae\u5e76\u9009\u62e9\"\u8fd0\u884c\u672c\u5730\u811a\u672c\"\u65f6\u53ef\u4ee5\u770b\u5230\u5b83\u3002

\u8fd0\u884c\u540e\uff0c\u60a8\u53ef\u4ee5\u770b\u5230\u7ed3\u679c\u3002

\u7136\u540e\uff0c\u60a8\u53ef\u4ee5\u901a\u8fc7 PC/\u7b14\u8bb0\u672c\u7535\u8111\u7684\u6d4f\u89c8\u5668\u8bbf\u95ee\u8be5\u7f51\u5740\u8fdb\u884c\u5f00\u53d1\uff0c\u5982\u4e0b\u56fe\u6240\u793a\u3002

\u9009\u62e9\u67d0\u4e2a\u9879\u76ee\u6216\u811a\u672c\u540e\uff0c\u60a8\u53ef\u4ee5\u5f00\u59cb\u5f00\u53d1

\u501f\u52a9\u5b83\uff0c\u60a8\u53ef\u4ee5\u7f16\u5199\u6d4f\u89c8\u5668\u4ee3\u7801\uff0c\u7136\u540e\u5728 Android \u624b\u673a\u4e0a\u8fd0\u884c\u3002\u975e\u5e38\u65b9\u4fbf\u3002

"},{"location":"editor-guide/#_3","title":"\u4ece\u60a8\u7684\u7535\u8111\u5f00\u53d1","text":"

\u9664\u4e86\u4e0a\u8ff0\u65b9\u6cd5\uff0c\u60a8\u8fd8\u53ef\u4ee5\u7528\u81ea\u5df1\u7684\u65b9\u5f0f\u5f00\u53d1\u811a\u672c\uff0c\u7136\u540e\u901a\u8fc7\u5185\u7f6e\u7684 FTP \u670d\u52a1\u5c06\u5176\u4e0a\u4f20\u5230\u624b\u673a\uff0c\u7528 QPython \u8fd0\u884c\u5b83\u3002

"},{"location":"external-api/","title":"QPython \u5f00\u653e API","text":"

QPython \u6709\u4e00\u4e2a\u5f00\u653e\u7684 activity\uff0c\u5141\u8bb8\u60a8\u4ece\u5916\u90e8\u8fd0\u884c qpython\u3002

MPyAPI \u7684\u5b9a\u4e49\u5982\u4e0b\uff1a

    <activity\n        android:name=\"org.qpython.qpylib.MPyApi\"\n        android:label=\"@string/qpy_run_with_share\"\n        android:screenOrientation=\"user\"\n        android:configChanges=\"orientation|keyboardHidden\"\n        android:exported=\"true\">\n        <intent-filter>\n            <action android:name=\"org.qpython.qpylib.action.MPyApi\" />\n            <category android:name=\"android.intent.category.DEFAULT\" />\n            <category android:name=\"android.intent.category.LAUNCHER\" />\n        </intent-filter>\n        <intent-filter>\n            <action android:name=\"android.intent.action.VIEW\" />\n            <category android:name=\"android.intent.category.DEFAULT\" />\n            <category android:name=\"android.intent.category.BROWSABLE\" />\n            <data android:scheme=\"http\" />\n            <data android:scheme=\"https\" />\n        </intent-filter>\n        <intent-filter>\n            <action android:name=\"android.intent.action.SEND\"/>\n            <category android:name=\"android.intent.category.DEFAULT\"/>\n            <data android:mimeType=\"text/plain\"/>\n        </intent-filter>\n        <intent-filter>\n            <action android:name=\"android.intent.action.SEND\"/>\n            <category android:name=\"android.intent.category.DEFAULT\"/>\n            <data android:mimeType=\"image/*\"/>\n        </intent-filter>\n    </activity>\n

\u56e0\u6b64\uff0c\u501f\u52a9\u5b83\uff0c\u60a8\u53ef\u4ee5\uff1a

"},{"location":"external-api/#qpython","title":"\u5c06\u5185\u5bb9\u5206\u4eab\u5230 QPython \u7684\u811a\u672c","text":"

\u60a8\u53ef\u4ee5\u5728\u67d0\u4e2a\u5e94\u7528\u4e2d\u9009\u62e9\u4e00\u4e9b\u5185\u5bb9\uff0c\u7136\u540e\u5206\u4eab\u5230 qpython \u7684\u811a\u672c\uff0c\u4e4b\u540e\u60a8\u53ef\u4ee5\u4f7f\u7528 sys.argv[2] \u5904\u7406\u8fd9\u4e9b\u5185\u5bb9

\u5728 YouTube \u4e0a\u89c2\u770b\u6f14\u793a\u89c6\u9891

"},{"location":"external-api/#qpython_1","title":"\u4ece\u60a8\u81ea\u5df1\u7684\u5e94\u7528\u7a0b\u5e8f\u8fd0\u884c QPython \u7684\u811a\u672c","text":"

\u60a8\u53ef\u4ee5\u901a\u8fc7\u8c03\u7528\u6b64 activity \u5728\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u4e2d\u8c03\u7528 QPython \u6765\u8fd0\u884c\u67d0\u4e9b\u811a\u672c\u6216 Python \u4ee3\u7801\uff0c\u793a\u4f8b\u5982\u4e0b\uff1a

// \u5c55\u793a\u5982\u4f55\u8c03\u7528 qpython API \u7684\u4ee3\u7801\u793a\u4f8b\nString extPlgPlusName = \"org.qpython.qpy\";          // QPython \u5305\u540d\nIntent intent = new Intent();\nintent.setClassName(extPlgPlusName, \"org.qpython.qpylib.MPyApi\");\nintent.setAction(extPlgPlusName + \".action.MPyApi\");\n\nBundle mBundle = new Bundle();\nmBundle.putString(\"app\", \"myappid\");\nmBundle.putString(\"act\", \"onPyApi\");\nmBundle.putString(\"flag\", \"onQPyExec\"); // \u60a8\u5728\u4e0a\u4e0b\u6587\u4e2d\u53ef\u80fd\u4f7f\u7528\u7684\u4efb\u610f\u5b57\u7b26\u4e32\u6807\u5fd7\nmBundle.putString(\"param\", \"\");         // \u60a8\u5728\u4e0a\u4e0b\u6587\u4e2d\u53ef\u80fd\u4f7f\u7528\u7684\u53c2\u6570\u5b57\u7b26\u4e32\n\n/*\n* \u6211\u4eec\u5c06\u8fd0\u884c\u7684 Python \u4ee3\u7801\n*/\nString code = \"import androidhelper\\n\" +\n            \"droid = androidhelper.Android()\\n\" +\n            \"line = droid.dialogGetInput()\\n\" +\n            \"s = 'Hello %s' % line.result\\n\" +\n            \"droid.makeToast(s)\\n\"\n\nmBundle.putString(\"pycode\", code);\nintent.putExtras(mBundle);\nstartActivityForResult(intent, SCRIPT_EXEC_PY);\n...\n\n// \u60a8\u53ef\u4ee5\u5728 onActivityResult \u4e2d\u5904\u7406 qpython \u8c03\u7528\u7ed3\u679c\n@Override\nprotected void onActivityResult(int requestCode, int resultCode, Intent data) {\n    if (requestCode == SCRIPT_EXEC_PY) {\n        if (data!=null) {\n            Bundle bundle = data.getExtras();\n            String flag = bundle.getString(\"flag\");\n            String param = bundle.getString(\"param\");\n            String result = bundle.getString(\"result\"); // \u60a8\u7684 Pycode \u751f\u6210\u7684\u7ed3\u679c\n            Toast.makeText(this, \"onQPyExec: return (\"+result+\")\", Toast.LENGTH_SHORT).show();\n        } else {\n            Toast.makeText(this, \"onQPyExec: data is null\", Toast.LENGTH_SHORT).show();\n\n        }\n    }\n}\n

\u4ece github \u68c0\u51fa\u4e00\u4e2a\u5b8c\u6574\u9879\u76ee

\u8fd8\u6709\u4e00\u4e2a\u751f\u4ea7\u5e94\u7528\u7a0b\u5e8f - \u9762\u5411 Tasker \u7684 QPython \u63d2\u4ef6

"},{"location":"featured-courses/","title":"\u7cbe\u9009\u8bfe\u7a0b","text":"

\u5728\u8fd9\u91cc\uff0c\u6211\u4eec\u4e3a\u60a8\u4ecb\u7ecd\u4e00\u4e9b\u7531\u793e\u533a\u6210\u5458\u521b\u5efa\u7684\u7cbe\u54c1\u8bfe\u7a0b\u3002\u6bcf\u4e00\u95e8\u8bfe\u7a0b\u90fd\u51dd\u805a\u4e86\u521b\u4f5c\u8005\u7684\u5fc3\u8840\u548c\u72ec\u7279\u89c1\u89e3\uff0c\u5e0c\u671b\u80fd\u4e3a\u60a8\u7684\u5b66\u4e60\u4e4b\u65c5\u5e26\u6765\u542f\u53d1\u548c\u5e2e\u52a9\u3002

"},{"location":"featured-courses/#aipython-aipy","title":"\u4e0eAI\u534f\u4f5c\u7684Python\u7f16\u7a0b\u5165\u95e8 - AIPY\u7f16\u7a0b\u7cfb\u5217","text":"

\u8bfe\u7a0b\u7b80\u4ecb\uff1a \u4f60\u662f\u5426\u89c9\u5f97\u7f16\u7a0b\u5f88\u96be\uff1f\u5728 AI \u65f6\u4ee3\uff0c\u8fd9\u4e00\u5207\u6b63\u5728\u6539\u53d8\u3002\u672c\u8bfe\u7a0b\u5c06\u5e26\u4f60\u638c\u63e1 Python \u7f16\u7a0b\u57fa\u7840\uff0c\u540c\u65f6\u5b66\u4f1a\u4e0e AI \u9ad8\u6548\u534f\u4f5c\u3002\u72ec\u521b AI \u8f85\u52a9\u6559\u5b66\u6cd5\uff0c\u7cfb\u7edf\u8bb2\u89e3 Python \u6838\u5fc3\u77e5\u8bc6\uff0c\u6559\u4f1a\u4f60\u5982\u4f55\u5411 AI \u7cbe\u786e\u8868\u8fbe\u9700\u6c42\u3001\u534f\u540c\u8c03\u8bd5\u4ee3\u7801\u3002\u901a\u8fc7\u804a\u5929\u673a\u5668\u4eba\u3001\u6570\u636e\u5206\u6790\u52a9\u624b\u7b49\u5b9e\u6218\u9879\u76ee\uff0c\u5efa\u7acb\u4eba\u673a\u534f\u4f5c\u5de5\u4f5c\u6d41\uff0c\u8ba9\u4f60\u5f7b\u5e95\u6253\u7834\u5bf9\u4ee3\u7801\u7684\u754f\u60e7\u3002

\u521b\u4f5c\u6545\u4e8b\uff1a \u8fd9\u95e8\u8bfe\u662f\u5982\u4f55\u8bde\u751f\u7684\uff1f\u4ee5\u4e0b\u662f\u4f5c\u8005\u7684\u521b\u4f5c\u624b\u8bb0\uff1a

  • \u4e3a\u4ec0\u4e48\u6211\u8981\u5199\u4e00\u5957\u9002\u5408\u521d\u5b66\u8005\u7684 AI \u534f\u52a9 Python \u7f16\u7a0b\u6559\u6750\uff1f - \u521b\u4f5c\u624b\u8bb0\uff08\u4e00\uff09
  • \u4e0e AI \u534f\u4f5c\u7684\"\u62c9\u626f\"\u5f0f Python \u7f16\u7a0b\u8bfe\u7a0b\u7eb2\u9886\u521b\u4f5c - \u521b\u4f5c\u624b\u8bb0\uff08\u4e8c\uff09
"},{"location":"featured-courses/#_2","title":"\u6b22\u8fce\u53cd\u9988","text":"

\u5982\u679c\u60a8\u6709\u60f3\u6cd5\u60f3\u8981\u5206\u4eab\uff0c\u6b22\u8fce\u8054\u7cfb\u6211\u4eec\uff01

  • GitHub Issues \u2013 \u63d0\u4ea4\u60a8\u7684\u8bfe\u7a0b\u5efa\u8bae
  • \u4e2d\u6587\u4ea4\u6d41\u793e\u533a \u2013 \u4e0e\u5176\u4ed6\u5b66\u4e60\u8005\u4ea4\u6d41\u5fc3\u5f97
"},{"location":"getting-started/","title":"QPython\uff1a\u5165\u95e8\u6307\u5357","text":"

\u672c\u6307\u5357\u5c06\u4ecb\u7ecd QPython \u7684\u529f\u80fd\u5e76\u5e2e\u52a9\u60a8\u5feb\u901f\u5165\u95e8\u3002

"},{"location":"getting-started/#qpython_1","title":"QPython \u6982\u8ff0","text":"

\u4e3a\u4ec0\u4e48\u9009\u62e9 QPython\uff1f

\u667a\u80fd\u624b\u673a\u5df2\u6210\u4e3a\u4eba\u4eec\u5fc5\u5907\u7684\u4fe1\u606f\u548c\u6280\u672f\u52a9\u624b\uff0c\u4e00\u4e2a\u7075\u6d3b\u7684\u89e3\u91ca\u5668\u5f15\u64ce\u53ef\u4ee5\u5e2e\u52a9\u60a8\u9ad8\u6548\u5b8c\u6210\u5927\u90e8\u5206\u5de5\u4f5c\uff0c\u65e0\u9700\u590d\u6742\u7684\u5f00\u53d1\u8fc7\u7a0b\u3002

QPython \u63d0\u4f9b\u4e86 \u60ca\u4eba\u7684\u5f00\u53d1\u4f53\u9a8c\u2014\u2014\u501f\u52a9\u5b83\u7684\u5e2e\u52a9\uff0c\u60a8\u53ef\u4ee5\u8f7b\u677e\u5b9e\u73b0\u7a0b\u5e8f\uff0c\u65e0\u9700\u590d\u6742\u7684 IDE \u5b89\u88c5\u3001\u7f16\u8bd1\u6216\u6253\u5305\u8fc7\u7a0b\u3002

"},{"location":"getting-started/#qpython_2","title":"QPython \u7248\u672c","text":"

\u9488\u5bf9\u4e0d\u540c\u7684\u4f7f\u7528\u573a\u666f\uff0cQPython \u6709\u591a\u4e2a\u7248\u672c\uff1a

  • QPython \u2013 \u7531 QPython \u56e2\u961f\u7ef4\u62a4\u7684\u4e3b\u8981\u7248\u672c\uff0c\u5177\u5907 AI \u529f\u80fd\uff0c\u53ef\u5728 Google Play \u7b49\u5e94\u7528\u5546\u5e97\u4e0b\u8f7d
  • QPython+ \u2013 \u7531\u5f00\u6e90\u8d21\u732e\u8005\u63a8\u51fa\u7684\u793e\u533a\u7248\uff0c\u63d0\u4f9b\u5404\u79cd\u65b0\u7279\u6027
  • QPython Plus \u2013 \u6269\u5c55\u6743\u9650\u7248\u672c\uff08\u4e0d\u5728\u5e94\u7528\u5546\u5e97\u4e0a\u67b6\uff09
"},{"location":"getting-started/#_1","title":"\u4e3b\u8981\u7279\u6027","text":"
  • \u79bb\u7ebf Python 3.12 \u89e3\u91ca\u5668 - \u8fd0\u884c Python \u7a0b\u5e8f\u65e0\u9700\u4e92\u8054\u7f51
  • QSL4A \u96c6\u6210 - \u4f7f\u7528 Python \u63a7\u5236 Android \u786c\u4ef6\u548c API
  • GenAI \u80fd\u529b\u96c6\u6210 - \u652f\u6301\u672c\u5730\u8fd0\u884c\u7684 LLM\u3001OpenAI \u7b49\u5404\u79cd LLM \u5e93\uff0c\u4ee5\u53ca\u53ef\u5728 QPython \u4e0a\u8fdb\u884c Vibe Coding \u5f00\u53d1\u7684 AIPyApp
  • \u6269\u5c55\u5305\u5b89\u88c5 - \u652f\u6301\u901a\u8fc7 QPYPI \u548c pip \u5b89\u88c5\u6269\u5c55\u5305
  • \u5185\u7f6e\u7f16\u8f91\u5668 - \u8bed\u6cd5\u9ad8\u4eae\u548c\u4ee3\u7801\u7f16\u8f91
  • \u591a\u79cd\u8fd0\u884c\u6a21\u5f0f - \u9664\u63a7\u5236\u53f0\u7a0b\u5e8f\u5916\uff0c\u8fd8\u652f\u6301 Android \u539f\u751f UI\uff08\u901a\u8fc7 QSL4A \u63a5\u53e3\uff09\u4ee5\u53ca Pygame / Turtle / Tkinter \u7b49\u8fd0\u884c\u65b9\u5f0f
"},{"location":"getting-started/#1","title":"1. \u4eea\u8868\u76d8","text":"

\u5b89\u88c5 QPython \u540e\uff0c\u70b9\u51fb\u5176\u56fe\u6807\u542f\u52a8\u3002\u60a8\u5c06\u770b\u5230\u5e26\u6709 QPython \u6807\u5fd7\u548c\u4ee5\u4e0b\u529f\u80fd\u7684\u4e3b\u4eea\u8868\u76d8\uff1a

"},{"location":"getting-started/#_2","title":"\u4eea\u8868\u76d8\u529f\u80fd","text":"

QPython \u4eea\u8868\u76d8\u63d0\u4f9b\u5bf9\u6240\u6709\u4e3b\u8981\u529f\u80fd\u7684\u5feb\u901f\u8bbf\u95ee\uff1a

  • \u7ec8\u7aef \u2014 \u8bbf\u95ee Python \u63a7\u5236\u53f0\u548c shell \u4ee5\u76f4\u63a5\u6267\u884c\u547d\u4ee4
  • Notebook \u2014 \u7528\u4e8e\u6570\u636e\u5206\u6790\u548c\u5b9e\u9a8c\u7684\u4ea4\u4e92\u5f0f Jupyter \u98ce\u683c\u7b14\u8bb0\u672c
  • \u7f16\u8f91\u5668 \u2014 \u5185\u7f6e\u4ee3\u7801\u7f16\u8f91\u5668\uff0c\u5177\u6709\u8bed\u6cd5\u9ad8\u4eae\u529f\u80fd\uff0c\u7528\u4e8e\u7f16\u5199 Python \u811a\u672c
  • \u8d44\u6e90\u7ba1\u7406\u5668 \u2014 \u6d4f\u89c8\u548c\u7ba1\u7406\u60a8\u7684\u6587\u4ef6\u3001\u811a\u672c\u548c\u9879\u76ee
  • QPYPI \u2014 \u5b89\u88c5 Python \u5305\u548c\u6269\u5c55\u3002\u8be6\u89c1 QPYPI \u6307\u5357
  • \u8bbe\u7f6e \u2014 \u914d\u7f6e QPython \u9996\u9009\u9879\u548c\u8fd0\u884c\u9009\u9879
  • \u793e\u533a \u2014 \u8bbf\u95ee QPython \u793e\u533a\u8d44\u6e90\u3001\u8bba\u575b\u548c\u5e2e\u52a9
  • \u8bfe\u7a0b \u2014 \u8bbf\u95ee Python \u7f16\u7a0b\u7684\u5b66\u4e60\u6750\u6599\u548c\u6559\u7a0b

\u70b9\u51fb\u4efb\u4f55\u56fe\u6807\u4ee5\u8bbf\u95ee\u76f8\u5e94\u7684\u529f\u80fd\u3002

"},{"location":"getting-started/#2","title":"2. \u7ec8\u7aef\u548c\u7f16\u8f91\u5668","text":""},{"location":"getting-started/#_3","title":"\u7ec8\u7aef","text":"

\u7ec8\u7aef\u63d0\u4f9b Python \u63a7\u5236\u53f0\uff0c\u652f\u6301\uff1a - \u63a2\u7d22\u5bf9\u8c61\u5c5e\u6027 - \u6d4b\u8bd5\u8bed\u6cd5\u548c\u60f3\u6cd5 - \u76f4\u63a5\u6267\u884c\u547d\u4ee4

\u4f7f\u7528\u52a0\u53f7\u6309\u94ae\uff081\uff09\u6253\u5f00\u65b0\u7ec8\u7aef\u6807\u7b7e\u9875\uff0c\u901a\u8fc7\u4e0b\u62c9\u83dc\u5355\uff082\uff09\u5207\u6362\uff0c\u5e76\u4f7f\u7528\u5173\u95ed\u6309\u94ae\uff083\uff09\u5173\u95ed\u3002

"},{"location":"getting-started/#_4","title":"\u7f16\u8f91\u5668","text":"

\u7f16\u8f91\u5668\u5e95\u90e8\u529f\u80fd\u680f\u5305\u542b\u4ee5\u4e0b\u5de5\u5177\uff08\u4ece\u5de6\u5230\u53f3\uff09\uff1a

  • \u5207\u6362\u5feb\u6377\u8f93\u5165\uff08\u5305\u542b def / if / else / elif / class \u7b49\u5173\u952e\u8bcd\uff09
  • \u9501\u5b9a\uff08\u9632\u6b62\u8bef\u89e6\uff09
  • \u8df3\u8f6c
  • \u4fdd\u5b58
  • \u8fd0\u884c
  • \u641c\u7d22
  • \u64a4\u9500
  • \u91cd\u505a
  • \u53e6\u5b58
  • \u6700\u8fd1\u6587\u4ef6
  • \u4ee3\u7801\u7247\u6bb5

\u91cd\u8981\u63d0\u793a\uff1a \u4fdd\u5b58\u65f6\u8bf7\u624b\u52a8\u6dfb\u52a0 .py \u6269\u5c55\u540d\uff0c\u56e0\u4e3a\u7f16\u8f91\u5668\u4e0d\u4f1a\u81ea\u52a8\u6dfb\u52a0\u3002

"},{"location":"getting-started/#3","title":"3. \u8d44\u6e90\u7ba1\u7406\u5668\uff08\u6587\u4ef6\u7ba1\u7406\uff09","text":"

\u901a\u8fc7 \u8d44\u6e90\u7ba1\u7406\u5668 \u8bbf\u95ee\u811a\u672c\u548c\u9879\u76ee\uff0c\u652f\u6301\u6d4f\u89c8\u3001\u7ec4\u7ec7\u548c\u7ba1\u7406\u6240\u6709 Python \u6587\u4ef6\u3002

"},{"location":"getting-started/#_5","title":"\u811a\u672c","text":"

\u811a\u672c\u662f\u5b58\u50a8\u5728 /storage/emulated/0/Android/data/org.qpython.qpy/files/scripts3/ \u4e2d\u7684\u5355\u4e2a Python \u6587\u4ef6\uff08\u9488\u5bf9 Python 3\uff09\u3002

\u53ef\u7528\u64cd\u4f5c\uff1a - \u8fd0\u884c \u2014 \u6267\u884c\u811a\u672c - \u6253\u5f00 \u2014 \u4f7f\u7528\u5185\u7f6e\u7f16\u8f91\u5668\u7f16\u8f91 - \u91cd\u547d\u540d \u2014 \u66f4\u6539\u811a\u672c\u540d\u79f0 - \u5220\u9664 \u2014 \u5220\u9664\u811a\u672c

"},{"location":"getting-started/#_6","title":"\u9879\u76ee","text":"

\u9879\u76ee\u662f\u5305\u542b main.py \u4f5c\u4e3a\u5165\u53e3\u70b9\u7684\u76ee\u5f55\u3002\u60a8\u53ef\u4ee5\u5728\u540c\u4e00\u76ee\u5f55\u4e2d\u5305\u542b\u5176\u4ed6\u4f9d\u8d56\u9879\u548c\u8d44\u6e90\u3002\u5c06\u9879\u76ee\u5b58\u50a8\u5728 /storage/emulated/0/Android/data/org.qpython.qpy/files/projects3/ \u4e2d\u3002

"},{"location":"getting-started/#_7","title":"\u7b14\u8bb0\u672c","text":"

Jupyter \u98ce\u683c\u7684\u7b14\u8bb0\u672c\u4e5f\u901a\u8fc7\u8d44\u6e90\u7ba1\u7406\u5668\u8fdb\u884c\u7ba1\u7406\uff0c\u5b58\u50a8\u5728 /storage/emulated/0/Android/data/org.qpython.qpy/files/notebooks/ \u4e2d\u3002

\u53ef\u7528\u64cd\u4f5c\uff1a - \u8fd0\u884c \u2014 \u6267\u884c\u7b14\u8bb0\u672c - \u6253\u5f00 \u2014 \u63a2\u7d22\u7b14\u8bb0\u672c\u5185\u5bb9 - \u91cd\u547d\u540d \u2014 \u66f4\u6539\u7b14\u8bb0\u672c\u540d\u79f0 - \u5220\u9664 \u2014 \u5220\u9664\u7b14\u8bb0\u672c

"},{"location":"getting-started/#4","title":"4. \u5e93","text":"

\u901a\u8fc7\u5b89\u88c5\u7b2c\u4e09\u65b9\u5e93\u6765\u6269\u5c55 QPython \u7684\u529f\u80fd\u3002

"},{"location":"getting-started/#_8","title":"\u5305\u5b89\u88c5\u65b9\u6cd5","text":"

QPYPI\uff08\u63a8\u8350\uff09

\u4ece QPYPI \u5b89\u88c5\u9884\u7f16\u8bd1\u7684\u5e93\uff0c\u5305\u62ec numpy\u3001scipy \u7b49\u79d1\u5b66\u5305\u3002

\u8be6\u89c1 QPYPI \u6307\u5357\u3002

PIP \u5ba2\u6237\u7aef

\u901a\u8fc7 QPython \u7684 PIP \u5ba2\u6237\u7aef\u6216 QPYPI \u754c\u9762\u5b89\u88c5\u7eaf Python \u5e93\uff1a

pip install requests\n

\u9884\u7f16\u8bd1\u5305

\u5bf9\u4e8e\u5177\u6709 C/C++/Rust \u4f9d\u8d56\u7684\u5305\uff0c\u4f7f\u7528 QPython \u7684\u9884\u7f16\u8bd1\u5305\uff1a

pip install numpy-qpython\npip install scipy-aipy\n

\u8be6\u89c1 QPYPI \u6307\u5357 \u83b7\u53d6\u53ef\u7528\u5305\u7684\u5b8c\u6574\u5217\u8868\u3002

\u624b\u52a8\u5b89\u88c5

\u60a8\u4e5f\u53ef\u4ee5\u5c06\u5e93\u590d\u5236\u5230 /storage/emulated/0/Android/data/org.qpython.qpy/files/lib/python3.12/site-packages/\u3002

"},{"location":"getting-started/#5","title":"5. \u8fd0\u884c\u6a21\u5f0f","text":"

QPython \u652f\u6301\u591a\u79cd\u8fd0\u884c\u6a21\u5f0f\u4ee5\u6ee1\u8db3\u4e0d\u540c\u7684\u7528\u4f8b\uff1a

"},{"location":"getting-started/#_9","title":"\u63a7\u5236\u53f0\u6a21\u5f0f","text":"

\u5e38\u89c4 Python \u811a\u672c\u7684\u9ed8\u8ba4\u6a21\u5f0f\u3002

"},{"location":"getting-started/#qsl4a","title":"QSL4A \u6a21\u5f0f","text":"

\u901a\u8fc7 QSL4A \u5e93\u8c03\u7528 Android API \u7684\u811a\u672c\u3002

import androidhelper\n\ndroid = androidhelper.Android()\ndroid.makeToast('Hello Android!')\n

\u8be6\u89c1 QSL4A \u6587\u6863 \u83b7\u53d6\u5b8c\u6574\u7684 API \u53c2\u8003\u3002

"},{"location":"getting-started/#webapp","title":"WebApp \u6a21\u5f0f","text":"

\u4f7f\u7528\u540e\u7aef\u670d\u52a1\u5668\u521b\u5efa\u57fa\u4e8e Web \u7684\u5e94\u7528\u7a0b\u5e8f\u3002\u9700\u5728\u811a\u672c\u5f00\u5934\u6dfb\u52a0\u4ee5\u4e0b\u4e24\u884c headers\uff1a

#qpy:webapp:<\u9879\u76ee\u540d>\n#qpy://localhost:<web\u670d\u52a1\u4fa6\u542c\u7684\u7aef\u53e3>/<\u9ed8\u8ba4\u4e3b\u8def\u5f84>\n

\u4f8b\u5982\uff1a

#qpy:webapp:Hello QPython\n#qpy://localhost:8080/hello\n\nfrom bottle import route, run, Bottle\n\napp = Bottle()\n\n@route('/hello')\ndef hello():\n    return '<h1>Hello from QPython!</h1>'\n\nrun(app, host='localhost', port=8080)\n
"},{"location":"getting-started/#q","title":"Q \u6a21\u5f0f\uff08\u65e0\u63a7\u5236\u53f0\u6a21\u5f0f\uff09","text":"

\u9759\u9ed8\u6a21\u5f0f\u8fd0\u884c\u811a\u672c\uff0c\u4e0d\u663e\u793a\u63a7\u5236\u53f0\u3002\u9700\u5728\u811a\u672c\u5f00\u5934\u6dfb\u52a0 header\uff1a

#qpy:quiet\n\nimport time\n\nwhile True:\n    # \u60a8\u7684\u540e\u53f0\u4efb\u52a1\n    time.sleep(60)\n
\u5982\u679c\u9700\u8981\u8fd0\u884c\u5e26 GUI \u7684 QSL4A \u7a0b\u5e8f\u4e14\u4e0d\u5e0c\u671b\u663e\u793a\u63a7\u5236\u53f0\u4fe1\u606f\uff0c\u63a8\u8350\u4f7f\u7528\u6b64\u6a21\u5f0f\u3002

"},{"location":"getting-started/#6","title":"6. \u793e\u533a\u4e0e\u652f\u6301","text":"

\u8bbf\u95ee QPython.org \u83b7\u53d6\u6587\u6863\u3001\u7528\u6237\u793e\u533a\u53ca\u5e2e\u52a9\u95ee\u7b54\u3002

\u793e\u533a\u94fe\u63a5\uff1a - Facebook \u7fa4\u7ec4 - GitHub - \u95ee\u9898\u53cd\u9988

\u4e0b\u4e00\u6b65\uff1a - \u5c1d\u8bd5 Hello World \u6559\u7a0b - \u63a2\u7d22 QSL4A API \u4ee5\u96c6\u6210 Android - \u4e86\u89e3 QPython \u7248\u672c

"},{"location":"getting-started/#_10","title":"\u89c6\u9891\u4ecb\u7ecd","text":""},{"location":"getting-started/#_11","title":"\u4e0b\u4e00\u6b65","text":"

\u5982\u679c\u60a8\u5df2\u7ecf\u521d\u6b65\u4e86\u89e3\u4e86 QPython \u7684\u529f\u80fd\uff0c\u6b22\u8fce\u5f00\u59cb\u4f53\u9a8c\u7f16\u7a0b\u7684\u4e50\u8da3\uff01\u8bd5\u8bd5 Hello World \u6559\u7a0b \u8fc8\u51fa\u60a8\u7684\u7b2c\u4e00\u6b65\u3002

"},{"location":"qpypi-guide/","title":"QPYPI","text":"

\u60a8\u53ef\u4ee5\u901a\u8fc7\u5b89\u88c5\u5305\u6765\u6269\u5c55 QPython \u7684\u529f\u80fd\u3002

"},{"location":"qpypi-guide/#_1","title":"\u5305\u5b89\u88c5\u652f\u6301","text":""},{"location":"qpypi-guide/#python","title":"\u7eaf Python \u5305","text":"

QPython \u652f\u6301\u4f7f\u7528\u7eaf Python \u5f00\u53d1\u7684 Python \u5305\u3002\u60a8\u53ef\u4ee5\u76f4\u63a5\u901a\u8fc7 QPython \u7684 PIP \u5ba2\u6237\u7aef\u6216\u4eea\u8868\u76d8\u4e0a\u7684 QPYPI \u4f7f\u7528 pip install \u5b89\u88c5\u8fd9\u4e9b\u5305\u3002

"},{"location":"qpypi-guide/#_2","title":"\u9884\u7f16\u8bd1\u5305","text":"

\u5982\u679c\u67d0\u4e9b\u5305\uff08\u6216\u5b83\u4eec\u7684\u4f9d\u8d56\uff09\u662f\u7528 Rust/C/C++ \u5f00\u53d1\u7684\uff0cQPython \u65e0\u6cd5\u76f4\u63a5\u652f\u6301\u5b83\u4eec\uff0c\u56e0\u4e3a QPython \u4e0a\u6ca1\u6709\u7f16\u8bd1\u5668\u5de5\u5177\u94fe\u652f\u6301\u3002\u4f46\u662f\uff0cQPython \u56e2\u961f\u5df2\u7ecf\u9884\u7f16\u8bd1\u4e86\u4e00\u4e9b\u5e38\u7528\u5305\uff0c\u5e76\u5728 QPython \u7684 QPYPI/Extensions \u4e2d\u53d1\u5e03\uff0c\u4f9b\u7528\u6237\u8f7b\u677e\u5b89\u88c5\u3002

"},{"location":"qpypi-guide/#_3","title":"\u5b89\u88c5\u9884\u7f16\u8bd1\u5305","text":"

\u60a8\u53ef\u4ee5\u901a\u8fc7\u4ee5\u4e0b\u65b9\u5f0f\u5b89\u88c5\u9884\u7f16\u8bd1\u5305\uff1a

  1. \u901a\u8fc7 QPython \u5e94\u7528\uff1a\u76f4\u63a5\u4ece QPython \u5e94\u7528\u5185\u7684 QPYPI \u6216 Extensions \u5b89\u88c5
  2. \u901a\u8fc7 PyPI\uff1a\u8bbf\u95ee https://pypi.org/user/qpythonx/ \u67e5\u770b\u53ef\u7528\u7684\u5305
  3. \u901a\u8fc7 pip \u547d\u4ee4\uff1a
  4. pip install xxx-qpython - \u5e26 -qpython \u540e\u7f00\u7684\u5305
  5. pip install xxx-aipy - \u5e26 -aipy \u540e\u7f00\u7684\u5305\uff08\u901a\u5e38\u662f AI/ML \u76f8\u5173\u7684\u5305\uff09

\u6ce8\u610f\uff1a\u6211\u4eec\u901a\u5e38\u6839\u636e\u5305\u7684\u9884\u671f\u7528\u9014\u6dfb\u52a0\u8fd9\u4e9b\u540e\u7f00\u4e4b\u4e00\u3002

"},{"location":"qpypi-guide/#_4","title":"\u8bf7\u6c42\u65b0\u5305","text":"

\u5982\u679c\u60a8\u9700\u8981\u5f53\u524d\u4e0d\u652f\u6301\u7684\u5305\uff1a

  • \u5728 qpython.org \u9879\u76ee \u4e2d\u63d0\u51fa\u95ee\u9898
  • QPython \u56e2\u961f\u5c06\u8003\u8651\u9884\u7f16\u8bd1\u5e76\u5c06\u5176\u6dfb\u52a0\u5230\u4ed3\u5e93\u4e2d

\u83b7\u53d6\u66f4\u591a\u5e2e\u52a9\u548c\u53c2\u4e0e\u793e\u533a\u7684\u65b9\u5f0f\uff0c\u8bf7\u53c2\u9605 \u793e\u533a\u4e0e\u53cd\u9988 \u90e8\u5206\u3002

\u6ce8\u610f\uff1a\u7531\u4e8e\u4e0d\u540c\u7684\u8ba1\u7b97\u673a\u67b6\u6784\uff0c\u6211\u4eec\u65e0\u6cd5\u4fdd\u8bc1 QPYPI \u5305\u542b PyPI \u4e0a\u7684\u6240\u6709\u5305\u3002

"},{"location":"qpython-x/","title":"QPython \u7248\u672c","text":"

QPython \u662f Android \u7684 Python \u5f15\u64ce\u3002\u5b83\u5305\u542b\u4ee4\u4eba\u60ca\u53f9\u7684\u529f\u80fd\uff0c\u5982 Python \u89e3\u91ca\u5668\u3001\u8fd0\u884c\u73af\u5883\u3001\u7f16\u8f91\u5668\u3001QPYI \u548c\u96c6\u6210\u7684 SL4A\u3002\u5b83\u8ba9\u60a8\u53ef\u4ee5\u8f7b\u677e\u5730\u5728 Android \u4e0a\u4f7f\u7528 Python\u3002\u800c\u4e14\u5b83\u662f\u514d\u8d39\u7684\u3002

QPython \u5df2\u7ecf\u5728\u5168\u7403\u62e5\u6709\u6570\u767e\u4e07\u7528\u6237\uff0c\u4e5f\u662f\u4e00\u4e2a\u5f00\u6e90\u9879\u76ee\u3002

\u9488\u5bf9\u4e0d\u540c\u7684\u4f7f\u7528\u573a\u666f\uff0cQPython \u6709\u591a\u4e2a\u7248\u672c\uff1a

"},{"location":"qpython-x/#qpython_1","title":"QPython","text":"

\u6807\u51c6\u7248\uff1a\u9488\u5bf9 AI \u6027\u80fd\u548c\u901a\u7528\u5e94\u7528\u5546\u5e97\u517c\u5bb9\u6027\u4f18\u5316

\u4e3b\u8981\u7248\u672c\uff0c\u53ef\u5728 Google Play \u548c\u5176\u4ed6\u5e94\u7528\u5546\u5e97\u4e0b\u8f7d\u3002\u6b64\u7248\u672c\u4e13\u6ce8\u4e8e AI \u529f\u80fd\uff0c\u4f7f AI \u65f6\u4ee3\u7528\u6237\u66f4\u5bb9\u6613\u5b66\u4e60\u548c\u4f7f\u7528 Python\u3002

\u4e3b\u8981\u7279\u6027\uff1a - AI \u9a71\u52a8\u7684\u7f16\u7801\u8f85\u52a9\u548c\u5b66\u4e60\u5de5\u5177 - \u79bb\u7ebf Python 3.12 \u89e3\u91ca\u5668\uff1a\u8fd0\u884c Python \u7a0b\u5e8f\u65e0\u9700\u4e92\u8054\u7f51 - \u652f\u6301\u591a\u79cd\u9879\u76ee\u7c7b\u578b\uff1a\u63a7\u5236\u53f0\u3001SL4A\u3001WebApp - \u65b9\u4fbf\u7684\u4e8c\u7ef4\u7801\u8bfb\u53d6\u5668\uff0c\u7528\u4e8e\u5c06\u4ee3\u7801\u4f20\u8f93\u5230\u624b\u673a - QPYPI \u548c\u81ea\u5b9a\u4e49\u4ed3\u5e93\uff0c\u7528\u4e8e\u9884\u7f16\u8bd1 wheel \u5305 -\u6613\u4e8e\u4f7f\u7528\u7684\u8bed\u6cd5\u9ad8\u4eae\u7f16\u8f91\u5668 - \u5b8c\u5584\u7684\u6587\u6863\u548c\u793e\u533a\u652f\u6301

\u6743\u9650\uff1a \u4ec5\u9700\u57fa\u672c\u624b\u673a\u6743\u9650\u5373\u53ef\u5b89\u88c5\u3002

\u4e0b\u8f7d\uff1a \u53ef\u5728 Google Play \u548c\u4e3b\u8981\u5e94\u7528\u5546\u5e97\u4e0b\u8f7d\u3002

"},{"location":"qpython-x/#qpython_2","title":"QPython+","text":"

\u793e\u533a\u7248\uff1a\u516c\u5f00\u652f\u6301\u5404\u79cd\u793e\u533a\u9a71\u52a8\u7684\u529f\u80fd\uff1b\u53ef\u5728\u90e8\u5206\u5e94\u7528\u5546\u5e97\u4e0b\u8f7d\u3002

\u793e\u533a\u5f00\u6e90\u7248\u672c\uff08\u6b63\u5728\u89c4\u5212\u548c\u51c6\u5907\u4e2d\uff09\u3002\u6b64\u7248\u672c\u4e13\u4e3a\u60f3\u8981\u53c2\u4e0e QPython \u9879\u76ee\u5f00\u53d1\u7684\u8d21\u732e\u8005\u800c\u8bbe\u8ba1\uff0c\u652f\u6301\u9488\u5bf9\u4e0d\u540c\u5236\u9020\u5546\u7684\u5b9a\u5236\u3002

\u4e3b\u8981\u7279\u6027\uff1a - \u793e\u533a\u9a71\u52a8\u5f00\u53d1 - \u652f\u6301\u4f9b\u5e94\u5546\u5b9a\u5236 - \u66f4\u7075\u6d3b\u7684\u914d\u7f6e\u9009\u9879 - \u5f00\u653e\u4f9b\u8d21\u732e\u8005\u52a0\u5165\u5f00\u53d1

\u6743\u9650\uff1a \u4ec5\u9700\u57fa\u672c\u624b\u673a\u6743\u9650\u5373\u53ef\u5b89\u88c5\u3002

\u4e0b\u8f7d\uff1a \u5c06\u5728 Google Play \u548c\u4e3b\u8981\u5e94\u7528\u5546\u5e97\u4e0b\u8f7d\u3002

\u6ce8\u610f\uff1a \u6b64\u7248\u672c\u76ee\u524d\u6b63\u5728\u89c4\u5212\u51c6\u5907\u4e2d\u3002\u8bf7\u5173\u6ce8\u66f4\u65b0\uff01

"},{"location":"qpython-x/#qpython-plus","title":"QPython Plus","text":"

QPython+ \u5b8c\u5168\u8bbf\u95ee\u7248\uff1a\u6388\u4e88\u8c03\u7528\u6240\u6709 Android \u63a5\u53e3\u7684\u5b8c\u6574\u6743\u9650\u3002\u4e0b\u8f7d\u94fe\u63a5\u4ec5\u901a\u8fc7\u5b98\u65b9\u4e91\u76d8\u63d0\u4f9b\u3002

\u5177\u6709\u6269\u5c55\u6743\u9650\u7684\u7279\u6b8a\u7248\u672c\uff0c\u63d0\u4f9b\u5bf9\u8bbe\u5907\u7684\u6700\u5927\u63a7\u5236\u3002\u7531\u4e8e\u5176\u654f\u611f\u7684\u6743\u9650\u8981\u6c42\uff0c\u6b64\u7248\u672c\u4e0d\u4f1a\u5728\u5e94\u7528\u5546\u5e97\u4e0a\u53d1\u5e03\u3002

\u4e3b\u8981\u7279\u6027\uff1a - \u5b8c\u6574\u7684 SL4A API \u8bbf\u95ee\uff0c\u5305\u62ec\u654f\u611f\u529f\u80fd - SMS/\u901a\u8bdd\u63a7\u5236 API - \u9ad8\u7ea7\u7cfb\u7edf\u96c6\u6210 - \u6700\u5927\u7684\u8bbe\u5907\u63a7\u5236\u80fd\u529b

\u6743\u9650\uff1a \u9700\u8981\u5e7f\u6cdb\u7684\u6743\u9650\uff0c\u5305\u62ec\uff1a - \u84dd\u7259 - \u4f4d\u7f6e\uff08GPS\uff09 - \u8bfb\u53d6/\u53d1\u9001\u77ed\u4fe1 - \u6253\u7535\u8bdd - \u76f8\u673a\u548c\u9ea6\u514b\u98ce - \u7cfb\u7edf\u8bbe\u7f6e - \u4ee5\u53ca\u5176\u4ed6\u654f\u611f\u6743\u9650

\u4e0b\u8f7d\uff1a \u4e0d\u5728\u5e94\u7528\u5546\u5e97\u4e0a\u67b6\u3002\u4ec5\u901a\u8fc7\u7279\u6b8a\u6e20\u9053\u5206\u53d1\u3002

\u91cd\u8981\u63d0\u793a\uff1a QPython \u4e0d\u4f1a\u5728\u540e\u53f0\u672a\u7ecf\u60a8\u77e5\u6089\u4f7f\u7528\u8fd9\u4e9b\u6743\u9650\u3002\u5982\u679c\u4f7f\u7528 SL4A API \u65f6\u51fa\u73b0\u5f02\u5e38\uff0c\u8bf7\u68c0\u67e5\u7cfb\u7edf\u8bbe\u7f6e\u4e2d\u662f\u5426\u5df2\u542f\u7528\u76f8\u5173\u6743\u9650\u3002

"},{"location":"tutorial-hello-world/","title":"\u7f16\u5199\"Hello World\"","text":""},{"location":"tutorial-hello-world/#hello-world_1","title":"Hello world","text":"

\u597d\u7684\uff0c\u5728\u60a8\u5bf9 QPython \u6709\u4e86\u4e00\u5b9a\u7684\u4e86\u89e3\u4e4b\u540e\uff0c\u8ba9\u6211\u4eec\u5728 QPython \u4e2d\u521b\u5efa\u6211\u4eec\u7684\u7b2c\u4e00\u4e2a\u7a0b\u5e8f\u3002\u663e\u7136\uff0c\u5b83\u5c06\u662f helloworld.py\u3002;)

\u542f\u52a8 QPython\uff0c\u6253\u5f00\u7f16\u8f91\u5668\u5e76\u8f93\u5165\u4ee5\u4e0b\u4ee3\u7801\uff1a

#qpy:quiet\nimport androidhelper\n\ndroid = androidhelper.Android()\ndroid.makeToast('Hello, QPYTHON!')\n

\u6beb\u65e0\u7591\u95ee\uff0c\u5b83\u4e0e\u5176\u4ed6\u4efb\u4f55 hello-world \u7a0b\u5e8f\u5e76\u65e0\u4e0d\u540c\u3002\u6267\u884c\u65f6\uff0c\u5b83\u53ea\u662f\u5728\u5c4f\u5e55\u4e0a\u663e\u793a\u5f39\u51fa\u6d88\u606f\uff08\u89c1\u9876\u90e8\u622a\u56fe\uff09\u3002\u65e0\u8bba\u5982\u4f55\uff0c\u8fd9\u662f\u4e00\u4e2a\u5f88\u597d\u7684 QPython \u7a0b\u5e8f\u793a\u4f8b\u3002

"},{"location":"tutorial-hello-world/#_1","title":"\u4ee3\u7801\u7406\u89e3","text":"

\u5b83\u4ee5 import androidhelper \u5f00\u5934\u2014\u2014\u8fd9\u662f QPython \u4e2d\u6700\u6709\u7528\u7684\u6a21\u5757\uff0c\u5b83\u5c01\u88c5\u4e86 Python \u4e2d\u53ef\u7528\u7684\u51e0\u4e4e\u6240\u6709\u4e0e Android \u7684\u63a5\u53e3\u3002\u4efb\u4f55\u5728 QPython \u4e2d\u5f00\u53d1\u7684\u811a\u672c\u90fd\u4ee5\u8fd9\u4e2a\u8bed\u53e5\u5f00\u5934\uff08\u81f3\u5c11\u5982\u679c\u5b83\u58f0\u79f0\u8981\u4e0e\u7528\u6237\u901a\u4fe1\u7684\u8bdd\uff09\u3002\u5728\u6b64\u5904\u9605\u8bfb\u66f4\u591a\u5173\u4e8e Python \u5e93 \u548c import \u8bed\u53e5 \u7684\u5185\u5bb9\u3002

\u63a5\u4e0b\u6765\u6211\u4eec\u521b\u5efa\u4e00\u4e2a droid \u5bf9\u8c61\uff08\u5b9e\u9645\u4e0a\u662f\u4e00\u4e2a\u7c7b\uff09\uff0c\u5b83\u5bf9\u4e8e\u8c03\u7528 RPC \u51fd\u6570\u4ee5\u4e0e Android \u901a\u4fe1\u662f\u5fc5\u8981\u7684\u3002

\u6211\u4eec\u4ee3\u7801\u7684\u6700\u540e\u4e00\u884c\u8c03\u7528\u4e86\u8fd9\u6837\u7684\u51fd\u6570\uff0cdroid.makeToast()\uff0c\u5b83\u5728\u5c4f\u5e55\u4e0a\u663e\u793a\u4e00\u4e2a\u5c0f\u7684\u5f39\u51fa\u6d88\u606f\uff08\"toast\"\uff09\u3002

\u597d\u7684\uff0c\u8ba9\u6211\u4eec\u6dfb\u52a0\u4e00\u4e9b\u66f4\u591a\u7684\u529f\u80fd\u3002\u8ba9\u5b83\u8be2\u95ee\u7528\u6237\u540d\u5e76\u5411\u7528\u6237\u6253\u62db\u547c\u3002

"},{"location":"tutorial-hello-world/#_2","title":"\u66f4\u591a\u793a\u4f8b","text":"

\u6211\u4eec\u53ef\u4ee5\u4f7f\u7528 dialogGetInput \u8c03\u7528\u663e\u793a\u4e00\u4e2a\u5e26\u6709\u6807\u9898\u3001\u63d0\u793a\u3001\u7f16\u8f91\u5b57\u6bb5\u548c \u786e\u5b9a \u548c \u53d6\u6d88 \u6309\u94ae\u7684\u7b80\u5355\u5bf9\u8bdd\u6846\u3002\u7528\u4ee5\u4e0b\u4ee3\u7801\u66ff\u6362\u60a8\u7684\u6700\u540e\u4e00\u884c\u4ee3\u7801\u5e76\u5c06\u5176\u4fdd\u5b58\u4e3a hello1.py\uff1a

#qpy:quiet\nimport androidhelper\n\ndroid = androidhelper.Android()\nrespond = droid.dialogGetInput(\"Hello\", \"What is your name?\")\n

\u597d\u7684\uff0c\u6211\u8ba4\u4e3a\u5b83\u5e94\u8be5\u8fd4\u56de\u4efb\u4f55\u54cd\u5e94\uff0c\u4efb\u4f55\u7528\u6237\u53cd\u5e94\u3002\u8fd9\u5c31\u662f\u4e3a\u4ec0\u4e48\u6211\u5199 respond = ...\u3002\u4f46\u8fd9\u4e2a\u8c03\u7528\u5b9e\u9645\u4e0a\u8fd4\u56de\u4ec0\u4e48\uff1f\u8ba9\u6211\u4eec\u68c0\u67e5\u4e00\u4e0b\u3002\u53ea\u9700\u5728\u6700\u540e\u4e00\u884c\u540e\u6dfb\u52a0 print \u8bed\u53e5\uff1a

#qpy:quiet\nimport androidhelper\n\ndroid = androidhelper.Android()\nrespond = droid.dialogGetInput(\"Hello\", \"What is your name?\")\nprint(respond)\n

\u7136\u540e\u4fdd\u5b58\u5e76\u8fd0\u884c\u5b83...

\u54ce\u5440\uff01\u6ca1\u6709\u6253\u5370\uff1f\u522b\u62c5\u5fc3\u3002\u53ea\u9700\u62c9\u4e0b\u901a\u77e5\u680f\uff0c\u60a8\u5c31\u4f1a\u770b\u5230\"QPython Program Output: hello1.py\"\u2014\u2014\u70b9\u51fb\u5b83\uff01

\u5982\u60a8\u6240\u89c1\uff0cdroid.dialogGetInput() \u8fd4\u56de\u4e00\u4e2a\u5305\u542b\u4e09\u4e2a\u5b57\u6bb5\u7684 JSON \u5bf9\u8c61\u3002\u6211\u4eec\u53ea\u9700\u8981\u4e00\u4e2a\u2014\u2014result\uff0c\u5176\u4e2d\u5305\u542b\u7528\u6237\u5b9e\u9645\u8f93\u5165\u7684\u5185\u5bb9\u3002

\u8ba9\u6211\u4eec\u6dfb\u52a0\u811a\u672c\u7684\u53cd\u5e94\uff1a

#qpy:quiet\nimport androidhelper\n\ndroid = androidhelper.Android()\nrespond = droid.dialogGetInput(\"Hello\", \"What is your name?\")\nprint(respond)\nmessage = f'Hello, {respond.result}!'\ndroid.makeToast(message)\n

\u6700\u540e\u4e24\u884c\uff081\uff09\u683c\u5f0f\u5316\u6d88\u606f\uff0c\uff082\uff09\u4ee5 toast \u5f62\u5f0f\u5411\u7528\u6237\u663e\u793a\u6d88\u606f\u3002\u5982\u679c\u60a8\u4ecd\u7136\u4e0d\u77e5\u9053 f-string \u662f\u4ec0\u4e48\u610f\u601d\uff0c\u8bf7\u53c2\u9605 Python \u6587\u6863\u3002

\u54c7\uff01\u5b83\u5de5\u4f5c\u4e86\uff01;)

\u73b0\u5728\u6211\u8981\u5728\u90a3\u91cc\u6dfb\u52a0\u4e00\u4e9b\u903b\u8f91\u3002\u60f3\u4e00\u60f3\uff1a\u5982\u679c\u7528\u6237\u70b9\u51fb \u53d6\u6d88 \u6309\u94ae\uff0c\u6216\u8005\u70b9\u51fb \u786e\u5b9a \u4f46\u5c06\u8f93\u5165\u5b57\u6bb5\u7559\u7a7a\uff0c\u4f1a\u53d1\u751f\u4ec0\u4e48\uff1f

\u60a8\u53ef\u4ee5\u73a9\u8fd9\u4e2a\u7a0b\u5e8f\uff0c\u68c0\u67e5 respond \u53d8\u91cf\u5728\u6bcf\u79cd\u60c5\u51b5\u4e0b\u5305\u542b\u4ec0\u4e48\u3002

\u9996\u5148\uff0c\u6211\u60f3\u5c06\u7528\u6237\u8f93\u5165\u7684\u6587\u672c\u653e\u5165\u4e00\u4e2a\u5355\u72ec\u7684\u53d8\u91cf\u4e2d\uff1aname = respond.result\u3002\u7136\u540e\u6211\u68c0\u67e5\u5b83\uff0c\u5982\u679c\u5b83\u5305\u542b\u4efb\u4f55\u771f\u5b9e\u6587\u672c\uff0c\u5b83\u5c06\u88ab\u89c6\u4e3a\u540d\u79f0\u5e76\u7528\u4e8e\u95ee\u5019\u3002\u5426\u5219\uff0c\u5c06\u663e\u793a\u53e6\u4e00\u6761\u6d88\u606f\u3002\u5c06\u7b2c\u4e94\u884c message = f'Hello, {respond.result}!' \u66ff\u6362\u4e3a\u4ee5\u4e0b\u4ee3\u7801\uff1a

name = respond.result\nif name:\n    message = f'Hello, {name}!'\nelse:\n    message = \"Hey! And you're not very polite, %Username%!\"\n

\u4f7f\u7528\u5de5\u5177\u680f\u4e0a\u7684 < \u548c > \u6309\u94ae\u6765\u7f29\u8fdb/\u53d6\u6d88\u7f29\u8fdb if \u8bed\u53e5\u4e2d\u7684\u884c\uff08\u6216\u8005\u53ea\u9700\u4f7f\u7528\u7a7a\u683c/\u9000\u683c\u952e\uff09\u3002\u60a8\u53ef\u4ee5\u5728 \u6b64\u5904 \u9605\u8bfb\u66f4\u591a\u5173\u4e8e Python \u7f29\u8fdb\u7684\u5185\u5bb9\uff1bif \u8bed\u53e5\u5728 \u6b64\u5904 \u63cf\u8ff0\u3002

\u9996\u5148\uff0c\u6211\u4eec\u5c06\u7528\u6237\u8f93\u5165\u653e\u5165 name \u53d8\u91cf\u3002\u7136\u540e\u6211\u4eec\u68c0\u67e5 name \u662f\u5426\u5305\u542b\u4efb\u4f55\u5185\u5bb9\uff1f\u5982\u679c\u7528\u6237\u7559\u7a7a\u884c\u5e76\u70b9\u51fb \u786e\u5b9a\uff0c\u8fd4\u56de\u503c\u662f\u7a7a\u5b57\u7b26\u4e32 ''\u3002\u5982\u679c\u6309\u4e0b\u4e86 \u53d6\u6d88 \u6309\u94ae\uff0c\u8fd4\u56de\u503c\u662f None\u3002\u5728 if \u8bed\u53e5\u4e2d\uff0c\u4e24\u8005\u90fd\u88ab\u89c6\u4e3a\u5047\u3002\u56e0\u6b64\uff0c\u53ea\u6709\u5f53 name \u5305\u542b\u4efb\u4f55\u6709\u610f\u4e49\u7684\u5185\u5bb9\u65f6\uff0cthen \u8bed\u53e5\u624d\u4f1a\u6267\u884c\uff0c\u5e76\u663e\u793a\u95ee\u5019\u8bed\"Hello, ...!\"\u3002\u5982\u679c\u8f93\u5165\u4e3a\u7a7a\uff0c\u7528\u6237\u5c06\u770b\u5230\"Hey! And you're not very polite, %Username%!\"\u6d88\u606f\u3002

\u597d\u7684\uff0c\u8fd9\u662f\u6574\u4e2a\u7a0b\u5e8f\uff1a

#qpy:quiet\nimport androidhelper\n\ndroid = androidhelper.Android()\nrespond = droid.dialogGetInput(\"Hello\", \"What is your name?\")\nprint(respond)\nname = respond.result\nif name:\n    message = f'Hello, {name}!'\nelse:\n    message = \"Hey! And you're not very polite, %Username%!\"\ndroid.makeToast(message)\n
"},{"location":"tutorial-hello-world/#_3","title":"\u8fd0\u884c\u6548\u679c","text":""},{"location":"tutorial-hello-world/#_4","title":"\u4e0b\u4e00\u6b65","text":"

\u5bf9\u4e8e Python \u65b0\u7528\u6237\uff0c\u63a8\u8350\u5b66\u4e60 Python \u8bed\u6cd5\u5b9e\u8df5 \u8bfe\u7a0b\u6765\u8fdb\u4e00\u6b65\u5b66\u4e60 Python\uff0c\u6216\u8005\u5728 QPython \u7cbe\u9009\u8bfe\u7a0b \u4e2d\u5bfb\u627e\u60f3\u8981\u5b66\u4e60\u7684\u5185\u5bb9\u3002

"},{"location":"whats-new/","title":"\u66f4\u65b0\u65e5\u5fd7","text":""},{"location":"whats-new/#v400","title":"v4.0.0","text":"
  • \u5916\u90e8\u5b58\u50a8\u8bbf\u95ee\uff1a\u7528\u6237\u73b0\u5728\u53ef\u4ee5\u5c06 Python \u811a\u672c\u76f4\u63a5\u4fdd\u5b58\u5230\u5916\u90e8\u5b58\u50a8\u8bbe\u5907\uff0c\u5927\u5927\u63d0\u5347\u4e86\u6587\u4ef6\u7ba1\u7406\u7684\u7075\u6d3b\u6027\u3002
  • QSL4A \u529f\u80fd\u589e\u5f3a\uff1a\u6539\u8fdb\u4e86 QSL4A \u7684\u529f\u80fd\u3002(https://www.qpython.org/en/qsl4a/)
  • \u793e\u533a\u4e0e\u8bfe\u7a0b\uff1a\u4f18\u5316\u4e86\u793e\u533a\u548c\u8bfe\u7a0b\u6a21\u5757\uff0c\u63d0\u4f9b\u66f4\u6e05\u6670\u7684\u4fe1\u606f\u548c\u66f4\u4fbf\u6377\u7684\u5bfc\u822a\uff0c\u65b9\u4fbf\u7528\u6237\u8bbf\u95ee\u5b66\u4e60\u8d44\u6e90\u548c\u83b7\u5f97\u652f\u6301\u3002
"},{"location":"whats-new/#v392-v393","title":"v3.9.2 / v3.9.3","text":"
  • SDK \u5347\u7ea7\uff0c\u652f\u6301 16 KB \u9875\u9762\u5927\u5c0f\uff0c\u63d0\u4f9b\u66f4\u6d41\u7545\u7684\u8fd0\u884c\u73af\u5883
  • \u7f16\u8f91\u5668\u91cd\u5927\u66f4\u65b0\uff0c\u5e26\u6765\u66f4\u6d41\u7545\u7684\u7f16\u8f91\u4f53\u9a8c
  • \u5176\u4ed6\u5404\u79cd\u5c0f\u6539\u8fdb
"},{"location":"whats-new/#v391","title":"v3.9.1","text":"
  • SDK \u5347\u7ea7\uff0c\u652f\u6301 16 KB \u9875\u9762\u5927\u5c0f\uff0c\u63d0\u4f9b\u66f4\u6d41\u7545\u7684\u8fd0\u884c\u73af\u5883
  • \u6269\u5c55\u5305\u73b0\u5728\u652f\u6301 MCP
  • \u9519\u8bef\u4fee\u590d
"},{"location":"whats-new/#v390","title":"v3.9.0","text":"
  • SDK \u5347\u7ea7\uff0c\u6574\u5408\u6700\u65b0\u7684 Android \u529f\u80fd
  • \u5185\u7f6e Ollama 0.9.5 \u5347\u7ea7\uff1a\u73b0\u5728\u652f\u6301\u5728 QPython \u4e2d\u672c\u5730\u8fd0\u884c Gemma3n \u6a21\u578b\uff0c\u4f53\u9a8c\u66f4\u5f3a\u5927\u7684\u7aef\u4fa7 AI \u8ba1\u7b97\u80fd\u529b
  • Pygame \u6e38\u620f\u5f00\u53d1\u652f\u6301\uff1a\u7ed3\u5408 XServer\uff0c\u60a8\u73b0\u5728\u53ef\u4ee5\u8f7b\u677e\u5730\u5728 QPython \u4e0a\u7f16\u5199\u548c\u8fd0\u884c Pygame \u6e38\u620f\uff0c\u91ca\u653e\u60a8\u7684\u521b\u9020\u529b\uff01
  • \u4e2a\u6027\u5316\u56fe\u6807\u81ea\u5b9a\u4e49\uff1a\u901a\u8fc7 QPython \u8bbe\u7f6e\u81ea\u5b9a\u4e49\u684c\u9762\u56fe\u6807\u548c\u4e3b\u9898\uff0c\u521b\u5efa\u72ec\u7279\u7684\u7f16\u7a0b\u73af\u5883
"},{"location":"whats-new/#v3811","title":"v3.8.11","text":"
  • \u5185\u7f6e Ollama 0.9.5 \u5347\u7ea7\uff1a\u73b0\u5728\u652f\u6301\u5728 QPython \u4e2d\u672c\u5730\u8fd0\u884c Gemma3n \u6a21\u578b\uff0c\u4f53\u9a8c\u66f4\u5f3a\u5927\u7684\u7aef\u4fa7 AI \u8ba1\u7b97\u80fd\u529b
  • Pygame \u6e38\u620f\u5f00\u53d1\u652f\u6301\uff1a\u7ed3\u5408 XServer\uff0c\u60a8\u73b0\u5728\u53ef\u4ee5\u8f7b\u677e\u5730\u5728 QPython \u4e0a\u7f16\u5199\u548c\u8fd0\u884c Pygame \u6e38\u620f\uff0c\u91ca\u653e\u60a8\u7684\u521b\u9020\u529b\uff01
  • \u4e2a\u6027\u5316\u56fe\u6807\u81ea\u5b9a\u4e49\uff1a\u901a\u8fc7 QPython \u8bbe\u7f6e\u81ea\u5b9a\u4e49\u684c\u9762\u56fe\u6807\u548c\u4e3b\u9898\uff0c\u521b\u5efa\u72ec\u7279\u7684\u7f16\u7a0b\u73af\u5883
"},{"location":"whats-new/#v3810","title":"v3.8.10","text":"
  • \u5185\u7f6e Ollama \u5347\u7ea7\uff1a\u73b0\u5728\u60a8\u53ef\u4ee5\u5728 QPython \u4e2d\u8f7b\u677e\u672c\u5730\u8fd0\u884c Qwen3 / Gemma3 \u6a21\u578b\uff01
  • \u6dfb\u52a0\u4e86\u5f3a\u5927\u7684 MCP \u5e93\uff1a\u5728 Extensions -> AIPY \u4e2d\u6dfb\u52a0\u4e86 mcp \u5e93\uff0c\u8ba9\u60a8\u968f\u65f6\u968f\u5730\u643a\u5e26\u5f3a\u5927\u7684 MCP \u529f\u80fd\uff01
"},{"location":"whats-new/#v389","title":"v3.8.9","text":"

\u91cd\u5927\u66f4\u65b0\uff01AI \u7f16\u7a0b\u5b8c\u5168\u96c6\u6210\u5230 QPython\uff0c\u8ba9\u60a8\u7684\u7f16\u7a0b\u66f4\u8f7b\u677e\uff01

  • \u81ea\u7136\u8bed\u8a00\u7f16\u7a0b\u652f\u6301\uff1a\u65b0\u652f\u6301 AIPyApp\uff0c\u5728 QPython \u4e2d\u5b9e\u73b0\u81ea\u7136\u8bed\u8a00\u7f16\u7a0b\u3002\u76ee\u524d\u5904\u4e8e\u6d4b\u8bd5\u9636\u6bb5 - \u52a0\u5165\u6211\u4eec\u7684\u793e\u533a\u83b7\u53d6\u4f7f\u7528\u8bf4\u660e
  • \u65b0 QSL4A \u529f\u80fd\uff1a\u6dfb\u52a0 notebookOpen \u51fd\u6570\uff0c\u652f\u6301\u81ea\u7136\u8bed\u8a00\u63a7\u5236\u6253\u5f00 Notebook \u6587\u4ef6
  • \u5185\u7f6e\u7f16\u8f91\u5668\u5347\u7ea7\uff1a\u589e\u5f3a\u7f16\u8f91\u5668\u529f\u80fd\uff0c\u652f\u6301\u6253\u5f00\u548c\u6d4f\u89c8\u5404\u79cd\u6587\u672c\u6587\u4ef6
  • \u4fbf\u6377\u7684\u6587\u4ef6\u7ba1\u7406\uff1a\u5728\u6587\u4ef6\u7ba1\u7406\u5668\u4e2d\u6dfb\u52a0\u5185\u90e8\u5b58\u50a8\u5165\u53e3\uff0c\u5feb\u901f\u8bbf\u95ee\u60a8\u7684\u6587\u4ef6
"},{"location":"whats-new/#v388","title":"v3.8.8","text":"
  • \u6587\u4ef6\u8bbf\u95ee\u6743\u9650\u63a7\u5236\u7684\u91cd\u8981\u6539\u8fdb\uff0c\u5141\u8bb8\u7528\u6237\u7075\u6d3b\u5730\u901a\u8fc7 QPython \u7f16\u7a0b\u542f\u7528\u6216\u7981\u7528\u5916\u90e8\u5b58\u50a8\u6587\u4ef6\u7684\u8bbf\u95ee
  • SDK \u5347\u7ea7\uff0c\u589e\u5f3a\u4e86\u5bf9\u65b0\u7248 Android \u7684\u652f\u6301\u548c\u517c\u5bb9\u6027
  • \u5728 Extensions -> AIPY \u4e2d\u6dfb\u52a0\u4e86 Anthropic \u548c Google GenAI \u5e93
"},{"location":"whats-new/#v387","title":"v3.8.7","text":"
  • \u5728\u73b0\u6709 Tkinter \u652f\u6301\u57fa\u7840\u4e0a\uff0c\u5728 Extensions -> Tools \u4e2d\u6dfb\u52a0\u4e86\u4f7f\u7528 Turtle \u7ed8\u5236\u54c6\u5566A\u68a6\u7684\u793a\u4f8b\u4ee3\u7801
  • \u4f18\u5316\u4e86\u624b\u673a\u6743\u9650\u83b7\u53d6\u6d41\u7a0b\uff0c\u63d0\u5347\u7528\u6237\u4f53\u9a8c\u548c\u64cd\u4f5c\u4fbf\u5229\u6027
  • \u5728 Extensions -> AIPY \u4e2d\u6dfb\u52a0\u4e86 Google Gen AI \u5e93\uff0c\u65b9\u4fbf\u8bbf\u95ee Gemini Developer API \u548c Vertex AI
"},{"location":"whats-new/#v386","title":"v3.8.6","text":"
  • Tkinter \u652f\u6301\uff1a\u73b0\u5728\u652f\u6301 Tkinter \u5e93\uff0c\u53ef\u901a\u8fc7 XServer \u8bbf\u95ee\u3002\u8bf7\u8bbf\u95ee www.qpython.org \u7684\u4e91\u76d8\"Extra\"\u94fe\u63a5\u4e0b\u8f7d\u6240\u9700\u6587\u4ef6
  • \u5b58\u50a8\u6743\u9650\u66f4\u65b0\uff1a\u4e3a\u6539\u5584\u7528\u6237\u4f53\u9a8c\uff0c\u6211\u4eec\u6dfb\u52a0\u4e86\u8bfb\u53d6\u624b\u673a\u5b58\u50a8\u7684\u6743\u9650\uff0c\u7528\u4e8e\u8bbf\u95ee\u5b58\u50a8\u5728\u5176\u4ed6\u76ee\u5f55\u4e2d\u7684 Python \u7a0b\u5e8f

\u6b64\u66f4\u65b0\u540e\uff0cQPython \u548c QPythonPlus\uff08\u4e0d\u5728\u5e94\u7528\u5546\u5e97\u4e0a\u67b6\uff0c\u4e13\u4e3a\u9700\u8981\u66f4\u591a\u654f\u611f\u6743\u9650\u7684\u9ad8\u7ea7\u7528\u6237\u8bbe\u8ba1\uff09\u5c06\u4fdd\u6301\u540c\u6b65\u7684\u7248\u672c\u53f7\u3002

"},{"location":"whats-new/#v352-2025225","title":"v3.5.2 (2025/2/25)","text":"

\u4e0e\u5f00\u6e90 LLM \u90e8\u7f72\u5de5\u5177 Ollama \u4ee5\u53ca DeepSeek \u5f00\u53d1\u7684 DeepSeek \u5b9e\u73b0\u65e0\u7f1d\u96c6\u6210\uff01

\u529f\u80fd\uff1a - \u96f6\u95e8\u69db\u5728\u79fb\u52a8\u8bbe\u5907\u4e0a\u672c\u5730\u8fd0\u884c\u5404\u79cd\u5927\u578b\u8bed\u8a00\u6a21\u578b - \u5feb\u901f\u90e8\u7f72 DeepSeek \u7b49\u5c16\u7aef AI \u6a21\u578b - \u4eab\u53d7\u7b80\u7ea6\u7684 API \u8c03\u7528\u4f53\u9a8c - \u6784\u5efa\u5b8c\u5168\u79bb\u7ebf\u7684\u667a\u80fd\u7f16\u7a0b\u73af\u5883

\u80fd\u529b\uff1a - \u76f4\u63a5\u5728\u624b\u673a\u4e0a\u52a0\u8f7d/\u7ba1\u7406 LLM \u6a21\u578b - \u57fa\u4e8e\u672c\u5730\u8ba1\u7b97\u7684\u5b9e\u65f6\u4f4e\u5ef6\u8fdf\u54cd\u5e94

"},{"location":"whats-new/#v350","title":"v3.5.0","text":"
  • Python \u5347\u7ea7\u5230 3.12.8
  • \u6539\u8fdb\u4eea\u8868\u76d8\uff0c\u529f\u80fd\u66f4\u6e05\u6670\u3001\u66f4\u4eba\u6027\u5316
  • \u6dfb\u52a0\u4e86\u4f17\u591a\u7b2c\u4e09\u65b9\u5305\uff1aPyTorch / Twisted / Scrapy / FastAPI ...
"},{"location":"whats-new/#v338","title":"v3.3.8","text":"
  • \u4fee\u590d\u4e86 NumPy \u517c\u5bb9\u6027\u95ee\u9898
"},{"location":"whats-new/#v335","title":"v3.3.5","text":"
  • \u5347\u7ea7 Python \u5185\u6838\u5230 3.11.9
  • \u4fee\u590d\u4e86\u6269\u5c55\u4e2d\u4e0d\u663e\u793a\u6a21\u5757\u5b89\u88c5\u72b6\u6001\u7684 bug
  • \u4fee\u590d\u4e86\u6269\u5c55\u4e2d\u5220\u9664\u6a21\u5757\u5931\u8d25\u7684 bug
  • \u4fee\u590d\u4e86\u5176\u4ed6 bug
"},{"location":"whats-new/#v334","title":"v3.3.4","text":"
  • \u6dfb\u52a0\u4e86 OpenAI/Langchain/APIGPTCloud AI \u5305
  • \u79fb\u9664\u4e0d\u5fc5\u8981\u7684\u6587\u4ef6\u4ee5\u51cf\u5c11\u5927\u5c0f
"},{"location":"whats-new/#v325","title":"v3.2.5","text":"
  • \u6dfb\u52a0\u4e86\u4e00\u4e9b SL4A \u51fd\u6570
  • \u5176\u4ed6\u9519\u8bef\u4fee\u590d
"},{"location":"whats-new/#v323","title":"v3.2.3","text":"
  • \u66f4\u65b0 Python \u7248\u672c\u5230 3.11.0
  • \u66f4\u65b0 IPython \u7248\u672c\u5230 8.6.0
  • \u66f4\u65b0 pip \u7248\u672c\u5230 22.3.1
  • \u66f4\u65b0\u79d1\u5b66\u8ba1\u7b97\u5305\uff0c\u81ea\u52a8\u8f6f\u94fe\u63a5
  • \u51cf\u5c11\u7a7a\u95f4\u4f7f\u7528
  • \u6dfb\u52a0\u4e86\u4e00\u4e9b SL4A \u51fd\u6570 + \u5176\u4ed6\u9519\u8bef\u4fee\u590d
"},{"location":"whats-new/#v318","title":"v3.1.8","text":"
  • \u5728\u7ec8\u7aef\u4e2d\u6dfb\u52a0\u4e86\u64cd\u4f5c\u70ed\u952e
  • \u4fee\u590d\u4e86\u7f16\u8f91\u5668\u5728\u65cb\u8f6c\u65f6\u4e22\u5931\u5185\u5bb9\u7684\u95ee\u9898
  • \u4fee\u590d\u4e86\u5176\u4ed6 bug

\u5728 Google Play \u4e0b\u8f7d

"},{"location":"qsl4a/","title":"QSL4A\uff08Android \u811a\u672c\u5c42\uff09API \u6587\u6863","text":"

QSL4A \u662f QPython \u7684 Android \u811a\u672c\u5c42\uff0c\u5141\u8bb8\u60a8\u4f7f\u7528 Python \u63a7\u5236 Android \u8bbe\u5907\u529f\u80fd\u3002

"},{"location":"qsl4a/#_1","title":"\u5feb\u901f\u5f00\u59cb","text":"
import androidhelper\n\ndroid = androidhelper.Android()\n\n# \u663e\u793a toast \u6d88\u606f\ndroid.makeToast('Hello QPython!')\n\n# \u9707\u52a8\u8bbe\u5907\ndroid.vibrate(500)\n\n# \u83b7\u53d6\u7535\u6c60\u7535\u91cf\uff08\u5148\u5f00\u59cb\u76d1\u63a7\uff09\nimport time\ndroid.batteryStartMonitoring()\ntime.sleep(0.5)  # \u7b49\u5f85\u6570\u636e\nbattery = droid.readBatteryData().result\nprint(f\"\u7535\u6c60\uff1a{battery['level']}%\")\n
"},{"location":"qsl4a/#_2","title":"\u6587\u6863\u7ed3\u6784","text":""},{"location":"qsl4a/#_3","title":"\u6838\u5fc3\u6a21\u5757","text":"
  • Android \u57fa\u7840 - \u6838\u5fc3\u8fde\u63a5\u548c RPC
  • Intent \u7cfb\u7edf - Android Intent \u64cd\u4f5c
  • \u4e8b\u4ef6\u7cfb\u7edf - \u4e8b\u4ef6\u5904\u7406\u548c\u5e7f\u64ad
"},{"location":"qsl4a/#ui","title":"UI \u7ec4\u4ef6","text":"
  • \u5bf9\u8bdd\u6846 - \u8b66\u62a5\u3001\u8f93\u5165\u3001\u9009\u62e9\u5bf9\u8bdd\u6846
  • \u5168\u5c4f UI - \u81ea\u5b9a\u4e49\u5e03\u5c40 UI
  • \u60ac\u6d6e\u7a97 - \u60ac\u6d6e\u7a97\u53e3
  • \u8f85\u52a9\u529f\u80fd - \u5c4f\u5e55\u81ea\u52a8\u5316
"},{"location":"qsl4a/#_4","title":"\u7cfb\u7edf","text":"
  • \u7535\u6c60 - \u7535\u6c60\u76d1\u63a7
  • \u4f20\u611f\u5668 - \u8bbe\u5907\u4f20\u611f\u5668
  • \u5e94\u7528 - \u5e94\u7528\u7ba1\u7406
  • \u7cfb\u7edf\u4fe1\u606f - \u8bbe\u5907\u4fe1\u606f
  • \u8bbe\u7f6e - \u7cfb\u7edf\u8bbe\u7f6e
  • \u5524\u9192\u9501 - \u5524\u9192\u9501\u63a7\u5236
  • QPython \u63a5\u53e3 - \u811a\u672c\u6267\u884c
  • Activity \u7ed3\u679c - Activity \u7ed3\u679c\u5904\u7406
"},{"location":"qsl4a/#_5","title":"\u786c\u4ef6","text":"
  • \u84dd\u7259 - \u84dd\u7259\u64cd\u4f5c
  • \u76f8\u673a - \u62cd\u7167\u548c\u5f55\u50cf
  • \u97f3\u9891/\u5f55\u97f3\u5668 - \u5f55\u97f3
  • \u7f51\u7edc\u6444\u50cf\u5934 - MJPEG \u6d41
  • USB \u4e32\u53e3 - USB \u4e3b\u673a\u4e32\u53e3
"},{"location":"qsl4a/#_6","title":"\u8fde\u63a5","text":"
  • WiFi - WiFi \u64cd\u4f5c
  • \u4f4d\u7f6e - GPS \u548c\u4f4d\u7f6e
  • \u77ed\u4fe1 - \u77ed\u4fe1\u64cd\u4f5c
  • \u7535\u8bdd - \u6253\u7535\u8bdd\u548c\u6765\u7535\u4fe1\u606f
  • \u8054\u7cfb\u4eba - \u8054\u7cfb\u4eba\u7ba1\u7406
  • \u4fe1\u53f7\u5f3a\u5ea6 - \u4fe1\u53f7\u76d1\u63a7
  • FTP \u670d\u52a1\u5668 - \u5185\u7f6e FTP \u670d\u52a1\u5668
"},{"location":"qsl4a/#_7","title":"\u5b58\u50a8","text":"
  • DocumentFile - \u6587\u4ef6\u64cd\u4f5c
  • \u526a\u8d34\u677f - \u526a\u8d34\u677f\u64cd\u4f5c
  • \u504f\u597d\u8bbe\u7f6e - \u5171\u4eab\u504f\u597d\u8bbe\u7f6e
"},{"location":"qsl4a/#_8","title":"\u5a92\u4f53","text":"
  • \u5a92\u4f53\u64ad\u653e\u5668 - \u97f3\u9891/\u89c6\u9891\u64ad\u653e
  • \u56fe\u50cf\u5904\u7406 - \u56fe\u50cf\u64cd\u4f5c
"},{"location":"qsl4a/#_9","title":"\u7279\u6b8a\u529f\u80fd","text":"
  • \u52a0\u5bc6 - \u52a0\u5bc6/\u89e3\u5bc6
  • PGPT AI - AI \u8bed\u97f3\u670d\u52a1
"},{"location":"qsl4a/#_10","title":"\u7ed3\u679c\u5bf9\u8c61","text":"

\u5927\u591a\u6570 QSL4A \u65b9\u6cd5\u8fd4\u56de\u4e00\u4e2a\u5e26\u6709\u4ee5\u4e0b\u5c5e\u6027\u7684 Result \u547d\u540d\u5143\u7ec4\uff1a - id - \u8bf7\u6c42 ID - result - \u5b9e\u9645\u7ed3\u679c\u6570\u636e - error - \u5982\u679c\u5931\u8d25\u5219\u4e3a\u9519\u8bef\u6d88\u606f

result = droid.getClipboard()\nif result.error is None:\n    print(result.result)\nelse:\n    print(f\"\u9519\u8bef\uff1a{result.error}\")\n
"},{"location":"qsl4a/connectivity/contacts/","title":"\u8054\u7cfb\u4eba API","text":"

\u8bbf\u95ee\u548c\u7ba1\u7406\u8bbe\u5907\u8054\u7cfb\u4eba\u3002

"},{"location":"qsl4a/connectivity/contacts/#_1","title":"\u9009\u62e9\u8054\u7cfb\u4eba","text":""},{"location":"qsl4a/connectivity/contacts/#pickcontact","title":"pickContact()","text":"

\u663e\u793a\u8054\u7cfb\u4eba\u5217\u8868\u4ee5\u4f9b\u9009\u62e9\u3002

pickContact()\n

\u8fd4\u56de\uff1a \u5305\u542b\u8054\u7cfb\u4eba URI \u7684 Intent

"},{"location":"qsl4a/connectivity/contacts/#pickphone","title":"pickPhone()","text":"

\u663e\u793a\u7535\u8bdd\u53f7\u7801\u5217\u8868\u4ee5\u4f9b\u9009\u62e9\u3002

pickPhone()\n

\u8fd4\u56de\uff1a \u9009\u4e2d\u7684\u7535\u8bdd\u53f7\u7801\u5b57\u7b26\u4e32

"},{"location":"qsl4a/connectivity/contacts/#_2","title":"\u8054\u7cfb\u4eba\u67e5\u8be2","text":""},{"location":"qsl4a/connectivity/contacts/#contactsget","title":"contactsGet()","text":"

\u83b7\u53d6\u6240\u6709\u8054\u7cfb\u4eba\u3002

contactsGet(attributes=None)\n

\u53c2\u6570\uff1a - attributes (list, optional): \u8981\u68c0\u7d22\u7684\u7279\u5b9a\u5c5e\u6027

\u8fd4\u56de\uff1a \u8054\u7cfb\u4eba JSONObject \u5217\u8868

"},{"location":"qsl4a/connectivity/contacts/#contactsgetbyid","title":"contactsGetById()","text":"

\u901a\u8fc7 ID \u83b7\u53d6\u8054\u7cfb\u4eba\u3002

contactsGetById(id, attributes=None)\n

\u53c2\u6570\uff1a - id (int): \u8054\u7cfb\u4eba ID - attributes (list, optional): \u8981\u68c0\u7d22\u7684\u7279\u5b9a\u5c5e\u6027

\u8fd4\u56de\uff1a JSONObject \u8054\u7cfb\u4eba\u6570\u636e

"},{"location":"qsl4a/connectivity/contacts/#contactsgetcount","title":"contactsGetCount()","text":"

\u83b7\u53d6\u8054\u7cfb\u4eba\u603b\u6570\u3002

contactsGetCount()\n

\u8fd4\u56de\uff1a \u6574\u6570\u8ba1\u6570

"},{"location":"qsl4a/connectivity/contacts/#contactsgetids","title":"contactsGetIds()","text":"

\u83b7\u53d6\u6240\u6709\u8054\u7cfb\u4eba ID\u3002

contactsGetIds()\n

\u8fd4\u56de\uff1a \u8054\u7cfb\u4eba ID \u6574\u6570\u5217\u8868

"},{"location":"qsl4a/connectivity/contacts/#contactsgetattributes","title":"contactsGetAttributes()","text":"

\u83b7\u53d6\u6240\u6709\u53ef\u80fd\u7684\u8054\u7cfb\u4eba\u5c5e\u6027\u3002

contactsGetAttributes()\n

\u8fd4\u56de\uff1a \u5c5e\u6027\u540d\u79f0\u5217\u8868

"},{"location":"qsl4a/connectivity/contacts/#_3","title":"\u5185\u5bb9\u67e5\u8be2","text":""},{"location":"qsl4a/connectivity/contacts/#querycontent","title":"queryContent()","text":"

\u4f7f\u7528\u81ea\u5b9a\u4e49\u53c2\u6570\u67e5\u8be2\u5185\u5bb9\u89e3\u6790\u5668\u3002

queryContent(uri, attributes=None, selection=None, selectionArgs=None, order=None)\n

\u53c2\u6570\uff1a - uri (str): \u5185\u5bb9 URI - attributes (list, optional): \u8981\u68c0\u7d22\u7684\u5c5e\u6027 - selection (str, optional): WHERE \u5b50\u53e5 - selectionArgs (list, optional): \u9009\u62e9\u53c2\u6570 - order (str, optional): ORDER BY \u5b50\u53e5

\u8fd4\u56de\uff1a JSONObject \u7ed3\u679c\u5217\u8868

"},{"location":"qsl4a/connectivity/contacts/#queryattributes","title":"queryAttributes()","text":"

\u83b7\u53d6\u5185\u5bb9 URI \u7684\u5c5e\u6027\u3002

queryAttributes(uri)\n

\u53c2\u6570\uff1a - uri (str): \u5185\u5bb9 URI

\u8fd4\u56de\uff1a \u5c5e\u6027\u540d\u79f0\u7684 JSONArray

"},{"location":"qsl4a/connectivity/contacts/#_4","title":"\u4f7f\u7528\u793a\u4f8b","text":"
import androidhelper\n\ndroid = androidhelper.Android()\n\n# \u9009\u62e9\u8054\u7cfb\u4eba\ncontact_uri = droid.pickContact().result\nprint(f\"Selected contact: {contact_uri}\")\n\n# \u9009\u62e9\u7535\u8bdd\u53f7\u7801\nphone = droid.pickPhone().result\nprint(f\"Selected phone: {phone}\")\n\n# \u83b7\u53d6\u6240\u6709\u8054\u7cfb\u4eba\ncontacts = droid.contactsGet().result\nprint(f\"Total contacts: {len(contacts)}\")\n\n# \u901a\u8fc7 ID \u83b7\u53d6\u8054\u7cfb\u4eba\ncontact = droid.contactsGetById(1).result\nprint(f\"Contact: {contact}\")\n\n# \u83b7\u53d6\u8054\u7cfb\u4eba\u5c5e\u6027\nattrs = droid.contactsGetAttributes().result\nprint(f\"Available attributes: {attrs}\")\n
"},{"location":"qsl4a/connectivity/ftp/","title":"FTP \u670d\u52a1\u5668 API","text":"

\u5728\u8bbe\u5907\u4e0a\u542f\u52a8\u548c\u7ba1\u7406\u5185\u7f6e FTP \u670d\u52a1\u5668\u3002

"},{"location":"qsl4a/connectivity/ftp/#ftp","title":"FTP \u670d\u52a1\u5668\u65b9\u6cd5","text":""},{"location":"qsl4a/connectivity/ftp/#ftpstart","title":"ftpStart()","text":"

\u542f\u52a8 FTP \u670d\u52a1\u5668\u3002

ftpStart()\n

\u8fd4\u56de\uff1a \u5305\u542b IP \u5730\u5740\u548c\u7aef\u53e3\u7684\u6570\u7ec4 [ip, port]

"},{"location":"qsl4a/connectivity/ftp/#ftpstop","title":"ftpStop()","text":"

\u505c\u6b62 FTP \u670d\u52a1\u5668\u3002

ftpStop()\n
"},{"location":"qsl4a/connectivity/ftp/#ftpisrunning","title":"ftpIsRunning()","text":"

\u68c0\u67e5 FTP \u670d\u52a1\u5668\u662f\u5426\u6b63\u5728\u8fd0\u884c\u3002

ftpIsRunning()\n

\u8fd4\u56de\uff1a \u5982\u679c\u6b63\u5728\u8fd0\u884c\u5219\u4e3a True

"},{"location":"qsl4a/connectivity/ftp/#ftpget","title":"ftpGet()","text":"

\u83b7\u53d6 FTP \u670d\u52a1\u5668 IP \u5730\u5740\u3002

ftpGet()\n

\u8fd4\u56de\uff1a \u5305\u542b IP \u5730\u5740\u548c\u7aef\u53e3\u7684\u6570\u7ec4

"},{"location":"qsl4a/connectivity/ftp/#ftpset","title":"ftpSet()","text":"

\u914d\u7f6e FTP \u670d\u52a1\u5668\u8bbe\u7f6e\u3002

ftpSet(port=None, rootDir=None, username=None, password=None)\n

\u53c2\u6570\uff1a - port (int, optional): \u670d\u52a1\u5668\u7aef\u53e3 - rootDir (str, optional): \u8981\u670d\u52a1\u7684\u57fa\u7840\u76ee\u5f55 - username (str, optional): \u767b\u5f55\u7528\u6237\u540d - password (str, optional): \u767b\u5f55\u5bc6\u7801

\u8fd4\u56de\uff1a \u5305\u542b\u5f53\u524d\u8bbe\u7f6e\u7684 JSONObject

"},{"location":"qsl4a/connectivity/ftp/#ftpstatus","title":"ftpStatus()","text":"

\u83b7\u53d6 FTP \u670d\u52a1\u5668\u72b6\u6001\u3002

ftpStatus()\n

\u8fd4\u56de\uff1a \u72b6\u6001\u63cf\u8ff0\u5b57\u7b26\u4e32

"},{"location":"qsl4a/connectivity/ftp/#_1","title":"\u4f7f\u7528\u793a\u4f8b","text":"
import androidhelper\n\ndroid = androidhelper.Android()\n\n# \u914d\u7f6e FTP \u670d\u52a1\u5668\ndroid.ftpSet(\n    port=2121,\n    rootDir=\"/sdcard\",\n    username=\"admin\",\n    password=\"secret\"\n)\n\n# \u542f\u52a8 FTP \u670d\u52a1\u5668\ninfo = droid.ftpStart().result\nprint(f\"FTP running at {info[0]}:{info[1]}\")\n\n# \u68c0\u67e5\u72b6\u6001\nif droid.ftpIsRunning().result:\n    print(\"FTP server is running\")\n\n# \u83b7\u53d6\u670d\u52a1\u5668\u4fe1\u606f\nserver_info = droid.ftpGet().result\nprint(f\"Server: {server_info}\")\n\n# \u5b8c\u6210\u540e\u505c\u6b62\ndroid.ftpStop()\n

\u6ce8\u610f\uff1a \u4f7f\u7528\u63d0\u4f9b\u7684\u51ed\u636e\u901a\u8fc7\u4efb\u4f55 FTP \u5ba2\u6237\u7aef\u8fde\u63a5\u5230 FTP \u670d\u52a1\u5668\u3002

"},{"location":"qsl4a/connectivity/location/","title":"\u4f4d\u7f6e API","text":"

\u8bbf\u95ee GPS \u548c\u7f51\u7edc\u4f4d\u7f6e\u670d\u52a1\u3002

"},{"location":"qsl4a/connectivity/location/#_1","title":"\u65b9\u6cd5","text":""},{"location":"qsl4a/connectivity/location/#startlocating","title":"startLocating()","text":"

\u5f00\u59cb\u4f4d\u7f6e\u66f4\u65b0\u3002

startLocating(minUpdateTime=60000, minUpdateDistance=30, updateGnssStatus=False)\n

\u53c2\u6570\uff1a - minUpdateTime (int): \u66f4\u65b0\u4e4b\u95f4\u7684\u6700\u5c0f\u65f6\u95f4\uff08\u6beb\u79d2\uff09\uff08\u9ed8\u8ba4\uff1a60000\uff09 - minUpdateDistance (float): \u66f4\u65b0\u7684\u6700\u5c0f\u8ddd\u79bb\uff08\u7c73\uff09\uff08\u9ed8\u8ba4\uff1a30\uff09 - updateGnssStatus (bool): \u542f\u7528 GNSS \u72b6\u6001\u66f4\u65b0\uff08\u9ed8\u8ba4\uff1aFalse\uff09

"},{"location":"qsl4a/connectivity/location/#stoplocating","title":"stopLocating()","text":"

\u505c\u6b62\u4f4d\u7f6e\u66f4\u65b0\u3002

stopLocating()\n
"},{"location":"qsl4a/connectivity/location/#readlocation","title":"readLocation()","text":"

\u83b7\u53d6\u6700\u540e\u5df2\u77e5\u4f4d\u7f6e\u3002

readLocation()\n

\u8fd4\u56de\uff1a \u4f4d\u7f6e\u6570\u636e\u5b57\u5178

"},{"location":"qsl4a/connectivity/location/#getlastknownlocation","title":"getLastKnownLocation()","text":"

\u83b7\u53d6\u7f13\u5b58\u7684\u4f4d\u7f6e\u3002

getLastKnownLocation()\n

\u8fd4\u56de\uff1a \u6765\u81ea\u6240\u6709\u63d0\u4f9b\u5546\u7684\u4f4d\u7f6e

"},{"location":"qsl4a/connectivity/location/#geocode","title":"geocode()","text":"

\u5c06\u5730\u5740\u8f6c\u6362\u4e3a\u5750\u6807\u3002

geocode(address, maxResults=1)\n
"},{"location":"qsl4a/connectivity/location/#_2","title":"\u4f4d\u7f6e\u63d0\u4f9b\u5546\u65b9\u6cd5)","text":""},{"location":"qsl4a/connectivity/location/#locationproviders","title":"locationProviders()","text":"

\u83b7\u53d6\u624b\u673a\u4e0a\u53ef\u7528\u7684\u4f4d\u7f6e\u63d0\u4f9b\u5546\u3002

locationProviders()\n

\u8fd4\u56de\uff1a \u53ef\u7528\u63d0\u4f9b\u5546\u540d\u79f0\u5217\u8868\uff08\u4f8b\u5982 ['gps', 'network']\uff09

"},{"location":"qsl4a/connectivity/location/#locationproviderenabled","title":"locationProviderEnabled()","text":"

\u68c0\u67e5\u7279\u5b9a\u4f4d\u7f6e\u63d0\u4f9b\u5546\u662f\u5426\u5df2\u542f\u7528\u3002

locationProviderEnabled(provider)\n

\u53c2\u6570\uff1a - provider (str): \u63d0\u4f9b\u5546\u540d\u79f0\uff08\u4f8b\u5982 'gps', 'network'\uff09

\u8fd4\u56de\uff1a \u5982\u679c\u5df2\u542f\u7528\u5219\u4e3a True\uff0c\u5426\u5219\u4e3a False

"},{"location":"qsl4a/connectivity/location/#readgnssstatus","title":"readGnssStatus()","text":"

\u8bfb\u53d6\u5168\u7403\u5bfc\u822a\u536b\u661f\u7cfb\u7edf\u72b6\u6001\uff08\u9700\u8981 Android 8+\uff09\u3002

readGnssStatus()\n

\u8fd4\u56de\uff1a \u5305\u542b GNSS \u536b\u661f\u4fe1\u606f\u7684 JSONArray

"},{"location":"qsl4a/connectivity/location/#_3","title":"\u4f7f\u7528\u793a\u4f8b","text":"
import androidhelper\nimport time\n\ndroid = androidhelper.Android()\n\n# \u5f00\u59cb\u4f4d\u7f6e\u66f4\u65b0\ndroid.startLocating(minUpdateTime=5000, minUpdateDistance=1, updateGnssStatus=False)\n\n# \u7b49\u5f85\u5b9a\u4f4d\ntime.sleep(10)\n\n# \u83b7\u53d6\u4f4d\u7f6e\nloc = droid.readLocation().result\nif loc:\n    lat = loc['latitude']\n    lon = loc['longitude']\n    print(f\"Location: {lat}, {lon}\")\n\ndroid.stopLocating()\n
"},{"location":"qsl4a/connectivity/phone/","title":"\u7535\u8bdd API","text":"

\u63a7\u5236\u7535\u8bdd\u901a\u8bdd\u548c\u83b7\u53d6\u7535\u8bdd\u4fe1\u606f\u3002

"},{"location":"qsl4a/connectivity/phone/#_1","title":"\u7535\u8bdd\u72b6\u6001\u8ddf\u8e2a","text":""},{"location":"qsl4a/connectivity/phone/#starttrackingphonestate","title":"startTrackingPhoneState()","text":"

\u5f00\u59cb\u8ddf\u8e2a\u7535\u8bdd\u72b6\u6001\u53d8\u5316\u3002\u751f\u6210 'phone' \u4e8b\u4ef6\u3002

startTrackingPhoneState()\n
"},{"location":"qsl4a/connectivity/phone/#readphonestate","title":"readPhoneState()","text":"

\u8bfb\u53d6\u5f53\u524d\u7535\u8bdd\u72b6\u6001\u3002

readPhoneState()\n

\u8fd4\u56de\uff1a \u5305\u542b\u7535\u8bdd\u72b6\u6001\u548c\u6765\u7535\u53f7\u7801\u7684 Bundle

"},{"location":"qsl4a/connectivity/phone/#stoptrackingphonestate","title":"stopTrackingPhoneState()","text":"

\u505c\u6b62\u8ddf\u8e2a\u7535\u8bdd\u72b6\u6001\u3002

stopTrackingPhoneState()\n
"},{"location":"qsl4a/connectivity/phone/#_2","title":"\u62e8\u6253\u7535\u8bdd","text":""},{"location":"qsl4a/connectivity/phone/#phonecall","title":"phoneCall()","text":"

\u901a\u8fc7 URI \u547c\u53eb\u8054\u7cfb\u4eba/\u7535\u8bdd\u53f7\u7801\u3002

phoneCall(uri)\n

\u53c2\u6570\uff1a - uri (str): \u8054\u7cfb\u4eba URI \u6216\u7535\u8bdd\u53f7\u7801 URI

"},{"location":"qsl4a/connectivity/phone/#phonecallnumber","title":"phoneCallNumber()","text":"

\u76f4\u63a5\u62e8\u6253\u7535\u8bdd\u53f7\u7801\u3002

phoneCallNumber(phone_number)\n

\u53c2\u6570\uff1a - phone_number (str): \u8981\u62e8\u6253\u7684\u7535\u8bdd\u53f7\u7801

"},{"location":"qsl4a/connectivity/phone/#phonedial","title":"phoneDial()","text":"

\u62e8\u6253\u53f7\u7801\uff08\u6253\u5f00\u62e8\u53f7\u5668\u4f46\u4e0d\u547c\u53eb\uff09\u3002

phoneDial(uri)\n

\u53c2\u6570\uff1a - uri (str): \u8054\u7cfb\u4eba URI \u6216\u7535\u8bdd\u53f7\u7801 URI

"},{"location":"qsl4a/connectivity/phone/#phonedialnumber","title":"phoneDialNumber()","text":"

\u62e8\u6253\u7535\u8bdd\u53f7\u7801\uff08\u6253\u5f00\u62e8\u53f7\u5668\u4f46\u4e0d\u547c\u53eb\uff09\u3002

phoneDialNumber(phone_number)\n

\u53c2\u6570\uff1a - phone_number (str): \u7535\u8bdd\u53f7\u7801

"},{"location":"qsl4a/connectivity/phone/#_3","title":"\u57fa\u7ad9\u4f4d\u7f6e","text":""},{"location":"qsl4a/connectivity/phone/#getcelllocation","title":"getCellLocation()","text":"

\u83b7\u53d6\u5f53\u524d\u57fa\u7ad9\u4f4d\u7f6e\u3002

getCellLocation()\n

\u8fd4\u56de\uff1a \u5305\u542b\u57fa\u7ad9\u4f4d\u7f6e\u6570\u636e\u7684 JSONObject

"},{"location":"qsl4a/connectivity/phone/#getallcellslocation","title":"getAllCellsLocation()","text":"

\u83b7\u53d6\u6240\u6709\u57fa\u7ad9\u4f4d\u7f6e\uff08\u9002\u7528\u4e8e\u53cc\u5361\u8bbe\u5907\uff09\u3002

getAllCellsLocation()\n

\u8fd4\u56de\uff1a \u57fa\u7ad9\u4f4d\u7f6e\u7684 JSONArray

"},{"location":"qsl4a/connectivity/phone/#_4","title":"\u7f51\u7edc\u4fe1\u606f","text":""},{"location":"qsl4a/connectivity/phone/#getnetworkoperator","title":"getNetworkOperator()","text":"

\u83b7\u53d6\u5f53\u524d\u8fd0\u8425\u5546\u7684 MCC+MNC\u3002

getNetworkOperator()\n

\u8fd4\u56de\uff1a \u5b57\u7b26\u4e32\uff08\u4f8b\u5982 '310260'\uff09

"},{"location":"qsl4a/connectivity/phone/#getnetworkoperatorname","title":"getNetworkOperatorName()","text":"

\u83b7\u53d6\u5f53\u524d\u8fd0\u8425\u5546\u7684\u540d\u79f0\u3002

getNetworkOperatorName()\n

\u8fd4\u56de\uff1a \u5b57\u7b26\u4e32\uff08\u4f8b\u5982 'T-Mobile'\uff09

"},{"location":"qsl4a/connectivity/phone/#getnetworktype","title":"getNetworkType()","text":"

\u83b7\u53d6\u5f53\u524d\u7f51\u7edc\u7c7b\u578b\u3002

getNetworkType()\n

\u8fd4\u56de\uff1a \u63cf\u8ff0\u65e0\u7ebf\u7535\u6280\u672f\u7684\u5b57\u7b26\u4e32\uff08\u4f8b\u5982 'LTE', 'UMTS', 'GSM'\uff09

"},{"location":"qsl4a/connectivity/phone/#getphonetype","title":"getPhoneType()","text":"

\u83b7\u53d6\u7535\u8bdd\u7c7b\u578b\u3002

getPhoneType()\n

\u8fd4\u56de\uff1a \u5b57\u7b26\u4e32\uff08\u4f8b\u5982 'GSM', 'CDMA', 'SIP'\uff09

"},{"location":"qsl4a/connectivity/phone/#sim","title":"SIM \u5361\u4fe1\u606f","text":""},{"location":"qsl4a/connectivity/phone/#getsimcountryiso","title":"getSimCountryIso()","text":"

\u83b7\u53d6 SIM \u5361\u7684 ISO \u56fd\u5bb6\u4ee3\u7801\u3002

getSimCountryIso()\n

\u8fd4\u56de\uff1a \u5b57\u7b26\u4e32\uff08\u4f8b\u5982 'us'\uff09

"},{"location":"qsl4a/connectivity/phone/#getsimoperator","title":"getSimOperator()","text":"

\u83b7\u53d6 SIM \u8fd0\u8425\u5546\u7684 MCC+MNC\u3002

getSimOperator()\n

\u8fd4\u56de\uff1a \u5b57\u7b26\u4e32\uff08\u4f8b\u5982 '310260'\uff09

"},{"location":"qsl4a/connectivity/phone/#getsimoperatorname","title":"getSimOperatorName()","text":"

\u83b7\u53d6 SIM \u8fd0\u8425\u5546\u540d\u79f0\u3002

getSimOperatorName()\n

\u8fd4\u56de\uff1a \u5b57\u7b26\u4e32\uff08\u4f8b\u5982 'T-Mobile'\uff09

"},{"location":"qsl4a/connectivity/phone/#getsimserialnumber","title":"getSimSerialNumber()","text":"

\u83b7\u53d6 SIM \u5e8f\u5217\u53f7\u3002

getSimSerialNumber()\n

\u8fd4\u56de\uff1a \u5b57\u7b26\u4e32 SIM \u5e8f\u5217\u53f7

"},{"location":"qsl4a/connectivity/phone/#getsimstate","title":"getSimState()","text":"

\u83b7\u53d6 SIM \u5361\u72b6\u6001\u3002

getSimState()\n

\u8fd4\u56de\uff1a \u63cf\u8ff0 SIM \u72b6\u6001\u7684\u5b57\u7b26\u4e32

"},{"location":"qsl4a/connectivity/phone/#getsubscriberid","title":"getSubscriberId()","text":"

\u83b7\u53d6\u8ba2\u9605\u8005 ID\u3002

getSubscriberId()\n

\u8fd4\u56de\uff1a \u5b57\u7b26\u4e32\u8ba2\u9605\u8005 ID

"},{"location":"qsl4a/connectivity/phone/#_5","title":"\u8bed\u97f3\u90ae\u4ef6","text":""},{"location":"qsl4a/connectivity/phone/#getvoicemailalphatag","title":"getVoiceMailAlphaTag()","text":"

\u83b7\u53d6\u8bed\u97f3\u90ae\u4ef6\u5b57\u6bcd\u6807\u7b7e\u3002

getVoiceMailAlphaTag()\n

\u8fd4\u56de\uff1a \u5b57\u7b26\u4e32\u8bed\u97f3\u90ae\u4ef6\u6807\u7b7e

"},{"location":"qsl4a/connectivity/phone/#getvoicemailnumber","title":"getVoiceMailNumber()","text":"

\u83b7\u53d6\u8bed\u97f3\u90ae\u4ef6\u53f7\u7801\u3002

getVoiceMailNumber()\n

\u8fd4\u56de\uff1a \u5b57\u7b26\u4e32\u8bed\u97f3\u90ae\u4ef6\u53f7\u7801

"},{"location":"qsl4a/connectivity/phone/#_6","title":"\u8bbe\u5907\u4fe1\u606f","text":""},{"location":"qsl4a/connectivity/phone/#getdeviceid","title":"getDeviceId()","text":"

\u83b7\u53d6\u8bbe\u5907 ID\uff08GSM \u7684 IMEI\uff09\u3002\u5df2\u5e9f\u5f03\u3002

getDeviceId()\n

\u8fd4\u56de\uff1a \u5b57\u7b26\u4e32\u8bbe\u5907 ID

"},{"location":"qsl4a/connectivity/phone/#getdevicesoftwareversion","title":"getDeviceSoftwareVersion()","text":"

\u83b7\u53d6\u8bbe\u5907\u8f6f\u4ef6\u7248\u672c\u3002

getDeviceSoftwareVersion()\n

\u8fd4\u56de\uff1a \u5b57\u7b26\u4e32\u8f6f\u4ef6\u7248\u672c

"},{"location":"qsl4a/connectivity/phone/#getline1number","title":"getLine1Number()","text":"

\u83b7\u53d6\u7ebf\u8def 1 \u7535\u8bdd\u53f7\u7801\u3002

getLine1Number()\n

\u8fd4\u56de\uff1a \u5b57\u7b26\u4e32\u7535\u8bdd\u53f7\u7801

"},{"location":"qsl4a/connectivity/phone/#checknetworkroaming","title":"checkNetworkRoaming()","text":"

\u68c0\u67e5\u662f\u5426\u8fde\u63a5\u5230\u6f2b\u6e38\u7f51\u7edc\u3002

checkNetworkRoaming()\n

\u8fd4\u56de\uff1a \u5982\u679c\u6b63\u5728\u6f2b\u6e38\u5219\u4e3a True

"},{"location":"qsl4a/connectivity/phone/#_7","title":"\u57fa\u7ad9\u4fe1\u606f","text":""},{"location":"qsl4a/connectivity/phone/#getallcellinfo","title":"getAllCellInfo()","text":"

\u83b7\u53d6\u6240\u6709\u57fa\u7ad9\u7684\u4fe1\u606f\u3002

getAllCellInfo()\n

\u8fd4\u56de\uff1a \u57fa\u7ad9\u4fe1\u606f\u5217\u8868

"},{"location":"qsl4a/connectivity/phone/#setdataenabled","title":"setDataEnabled()","text":"

\u542f\u7528\u6216\u7981\u7528\u79fb\u52a8\u6570\u636e\u3002

setDataEnabled(enabled)\n

\u53c2\u6570\uff1a - enabled (bool): True \u542f\u7528\uff0cFalse \u7981\u7528

"},{"location":"qsl4a/connectivity/phone/#_8","title":"\u4f7f\u7528\u793a\u4f8b","text":"
import androidhelper\n\ndroid = androidhelper.Android()\n\n# \u83b7\u53d6\u7f51\u7edc\u4fe1\u606f\noperator = droid.getNetworkOperatorName().result\nprint(f\"Operator: {operator}\")\n\nnetwork_type = droid.getNetworkType().result\nprint(f\"Network: {network_type}\")\n\n# \u83b7\u53d6 SIM \u4fe1\u606f\nsim_state = droid.getSimState().result\nprint(f\"SIM: {sim_state}\")\n\n# \u83b7\u53d6\u7535\u8bdd\u53f7\u7801\nline1 = droid.getLine1Number().result\nprint(f\"Phone: {line1}\")\n\n# \u8ddf\u8e2a\u7535\u8bdd\u72b6\u6001\ndroid.startTrackingPhoneState()\nprint(\"Tracking phone state...\")\ndroid.stopTrackingPhoneState()\n
"},{"location":"qsl4a/connectivity/signalstrength/","title":"\u4fe1\u53f7\u5f3a\u5ea6 API","text":"

\u76d1\u63a7\u8702\u7a9d\u548c\u65e0\u7ebf\u4fe1\u53f7\u5f3a\u5ea6\u3002

"},{"location":"qsl4a/connectivity/signalstrength/#_1","title":"\u4fe1\u53f7\u5f3a\u5ea6\u65b9\u6cd5","text":""},{"location":"qsl4a/connectivity/signalstrength/#starttrackingsignalstrengths","title":"startTrackingSignalStrengths()","text":"

\u5f00\u59cb\u8ddf\u8e2a\u4fe1\u53f7\u5f3a\u5ea6\u53d8\u5316\u3002\u751f\u6210 'signal_strengths' \u4e8b\u4ef6\u3002

startTrackingSignalStrengths()\n
"},{"location":"qsl4a/connectivity/signalstrength/#stoptrackingsignalstrengths","title":"stopTrackingSignalStrengths()","text":"

\u505c\u6b62\u8ddf\u8e2a\u4fe1\u53f7\u5f3a\u5ea6\u3002

stopTrackingSignalStrengths()\n
"},{"location":"qsl4a/connectivity/signalstrength/#readsignalstrengths","title":"readSignalStrengths()","text":"

\u8bfb\u53d6\u5f53\u524d\u4fe1\u53f7\u5f3a\u5ea6\u3002

readSignalStrengths()\n

\u8fd4\u56de\uff1a \u5305\u542b\u4fe1\u53f7\u5f3a\u5ea6\u6570\u636e\u7684 Bundle

"},{"location":"qsl4a/connectivity/signalstrength/#gettelephonesignalstrengthlevel","title":"getTelephoneSignalStrengthLevel()","text":"

\u83b7\u53d6\u7535\u8bdd\u4fe1\u53f7\u5f3a\u5ea6\u7b49\u7ea7\uff080-4\uff09\u3002

getTelephoneSignalStrengthLevel()\n

\u8fd4\u56de\uff1a \u6574\u6570\u7b49\u7ea7\uff080=\u65e0\uff0c1=\u5dee\uff0c2=\u4e00\u822c\uff0c3=\u597d\uff0c4=\u4f18\u79c0\uff09

"},{"location":"qsl4a/connectivity/signalstrength/#gettelephonesignalstrengthdetail","title":"getTelephoneSignalStrengthDetail()","text":"

\u83b7\u53d6\u8be6\u7ec6\u7684\u7535\u8bdd\u4fe1\u53f7\u5f3a\u5ea6\u4fe1\u606f\u3002

getTelephoneSignalStrengthDetail()\n

\u8fd4\u56de\uff1a \u5305\u542b\u8be6\u7ec6\u4fe1\u53f7\u4fe1\u606f\u7684\u5b57\u7b26\u4e32

"},{"location":"qsl4a/connectivity/signalstrength/#_2","title":"\u4f7f\u7528\u793a\u4f8b","text":"
import androidhelper\nimport time\n\ndroid = androidhelper.Android()\n\n# \u5f00\u59cb\u8ddf\u8e2a\u4fe1\u53f7\u5f3a\u5ea6\ndroid.startTrackingSignalStrengths()\n\n# \u7b49\u5f85\u4fe1\u53f7\u66f4\u65b0\ntime.sleep(5)\n\n# \u8bfb\u53d6\u5f53\u524d\u4fe1\u53f7\u5f3a\u5ea6\nsignal = droid.readSignalStrengths().result\nprint(f\"Signal: {signal}\")\n\n# \u76f4\u63a5\u83b7\u53d6\u7b49\u7ea7\nlevel = droid.getTelephoneSignalStrengthLevel().result\nprint(f\"Signal level: {level}/4\")\n\ndroid.stopTrackingSignalStrengths()\n
"},{"location":"qsl4a/connectivity/sms/","title":"SMS API","text":"

\u53d1\u9001\u548c\u63a5\u6536 SMS \u6d88\u606f\u3002

"},{"location":"qsl4a/connectivity/sms/#_1","title":"\u65b9\u6cd5","text":""},{"location":"qsl4a/connectivity/sms/#smssend","title":"smsSend()","text":"

\u53d1\u9001 SMS \u6d88\u606f\u3002

smsSend(destinationAddress, text)\n

\u53c2\u6570\uff1a - destinationAddress (str): \u7535\u8bdd\u53f7\u7801 - text (str): \u6d88\u606f\u6587\u672c

"},{"location":"qsl4a/connectivity/sms/#smsgetmessagecount","title":"smsGetMessageCount()","text":"

\u83b7\u53d6\u6d88\u606f\u6570\u91cf\u3002

smsGetMessageCount(unreadOnly=False, folder=\"inbox\")\n
"},{"location":"qsl4a/connectivity/sms/#smsgetmessageids","title":"smsGetMessageIds()","text":"

\u83b7\u53d6\u6d88\u606f ID\u3002

smsGetMessageIds(unreadOnly=False, folder=\"inbox\")\n
"},{"location":"qsl4a/connectivity/sms/#smsgetmessages","title":"smsGetMessages()","text":"

\u83b7\u53d6\u6d88\u606f\u8be6\u60c5\u3002

smsGetMessages(unreadOnly=False, folder=\"inbox\", attributes=None)\n
"},{"location":"qsl4a/connectivity/sms/#smsgetmessagebyid","title":"smsGetMessageById()","text":"

\u901a\u8fc7 ID \u83b7\u53d6\u7279\u5b9a\u6d88\u606f\u3002

smsGetMessageById(id, attributes=None)\n

\u53c2\u6570\uff1a - id (int): \u6d88\u606f ID - attributes (list, optional): \u8981\u68c0\u7d22\u7684\u7279\u5b9a\u5c5e\u6027

\u8fd4\u56de\uff1a \u6d88\u606f\u6570\u636e\u5b57\u5178

"},{"location":"qsl4a/connectivity/sms/#smsgetattributes","title":"smsGetAttributes()","text":"

\u83b7\u53d6\u53ef\u7528\u7684 SMS \u6d88\u606f\u5c5e\u6027\u3002

smsGetAttributes()\n

\u8fd4\u56de\uff1a \u53ef\u7528\u5c5e\u6027\u540d\u79f0\u5217\u8868

"},{"location":"qsl4a/connectivity/sms/#smsdeletemessage","title":"smsDeleteMessage()","text":"

\u5220\u9664\u6d88\u606f\u3002

smsDeleteMessage(id)\n
"},{"location":"qsl4a/connectivity/sms/#smsmarkmessageread","title":"smsMarkMessageRead()","text":"

\u5c06\u6d88\u606f\u6807\u8bb0\u4e3a\u5df2\u8bfb\u3002

smsMarkMessageRead(ids, read=True)\n
"},{"location":"qsl4a/connectivity/sms/#_2","title":"\u4f7f\u7528\u793a\u4f8b","text":"
import androidhelper\n\ndroid = androidhelper.Android()\n\n# \u53d1\u9001 SMS\ndroid.smsSend(\"+1234567890\", \"Hello from QPython!\")\n\n# \u83b7\u53d6\u672a\u8bfb\u6d88\u606f\nmessages = droid.smsGetMessages(unreadOnly=True).result\nfor msg in messages:\n    print(f\"From: {msg['address']}, Text: {msg['body']}\")\n
"},{"location":"qsl4a/connectivity/wifi/","title":"WiFi API","text":"

\u63a7\u5236 WiFi \u9002\u914d\u5668\u5e76\u83b7\u53d6\u8fde\u63a5\u4fe1\u606f\u3002

"},{"location":"qsl4a/connectivity/wifi/#_1","title":"\u9002\u914d\u5668\u63a7\u5236","text":""},{"location":"qsl4a/connectivity/wifi/#checkwifistate","title":"checkWifiState()","text":"

\u68c0\u67e5 WiFi \u662f\u5426\u5df2\u542f\u7528\u3002

checkWifiState()\n

\u8fd4\u56de\uff1a \u5982\u679c WiFi \u5df2\u542f\u7528\u5219\u4e3a True\uff0c\u5426\u5219\u4e3a False

"},{"location":"qsl4a/connectivity/wifi/#togglewifistate","title":"toggleWifiState()","text":"

\u6253\u5f00\u6216\u5173\u95ed WiFi\u3002

toggleWifiState(enabled=None)\n

\u53c2\u6570\uff1a - enabled (bool): True \u542f\u7528\uff0cFalse \u7981\u7528\uff0cNone \u5207\u6362

\u8fd4\u56de\uff1a \u5982\u679c\u64cd\u4f5c\u6210\u529f\u5219\u4e3a True

"},{"location":"qsl4a/connectivity/wifi/#wifistartscan","title":"wifiStartScan()","text":"

\u5f00\u59cb\u626b\u63cf\u53ef\u7528\u7684 WiFi \u7f51\u7edc\u3002

wifiStartScan()\n
"},{"location":"qsl4a/connectivity/wifi/#wifigetscanresults","title":"wifiGetScanResults()","text":"

\u83b7\u53d6\u53d1\u73b0\u7684 WiFi \u7f51\u7edc\u5217\u8868\u3002

wifiGetScanResults()\n

\u8fd4\u56de\uff1a \u63a5\u5165\u70b9\u4fe1\u606f\u5217\u8868

"},{"location":"qsl4a/connectivity/wifi/#_2","title":"\u8fde\u63a5\u7ba1\u7406","text":""},{"location":"qsl4a/connectivity/wifi/#wifigetconnectioninfo","title":"wifiGetConnectionInfo()","text":"

\u83b7\u53d6\u8be6\u7ec6\u8fde\u63a5\u4fe1\u606f\u3002

wifiGetConnectionInfo()\n

\u8fd4\u56de\uff1a \u5305\u542b SSID\u3001BSSID\u3001IP \u5730\u5740\u7b49\u8fde\u63a5\u8be6\u60c5\u7684\u5b57\u5178

"},{"location":"qsl4a/connectivity/wifi/#getconnectedinfo","title":"getConnectedInfo()","text":"

\u83b7\u53d6\u8fde\u63a5\u7684 WiFi \u7f51\u7edc\u4fe1\u606f\uff08\u7b80\u5316\u7248\uff09\u3002

getConnectedInfo()\n

\u8fd4\u56de\uff1a \u5305\u542b SSID\u3001BSSID\u3001\u4fe1\u53f7\u5f3a\u5ea6\u7684\u5b57\u5178

"},{"location":"qsl4a/connectivity/wifi/#getdhcpinfo","title":"getDhcpInfo()","text":"

\u83b7\u53d6\u5f53\u524d\u8fde\u63a5\u7684 DHCP \u4fe1\u606f\u3002

getDhcpInfo(ipConvertToString=True)\n

\u53c2\u6570\uff1a - ipConvertToString (bool): \u5c06 IP \u5730\u5740\u8f6c\u6362\u4e3a\u5b57\u7b26\u4e32\u683c\u5f0f\uff08\u9ed8\u8ba4\uff1aTrue\uff09

\u8fd4\u56de\uff1a \u5305\u542b IP\u3001\u7f51\u5173\u3001DNS \u7b49 DHCP \u4fe1\u606f\u7684\u5b57\u5178

"},{"location":"qsl4a/connectivity/wifi/#wifidisconnect","title":"wifiDisconnect()","text":"

\u65ad\u5f00\u5f53\u524d WiFi \u7f51\u7edc\u8fde\u63a5\u3002

wifiDisconnect()\n
"},{"location":"qsl4a/connectivity/wifi/#wifireconnect","title":"wifiReconnect()","text":"

\u91cd\u65b0\u8fde\u63a5\u5230\u5f53\u524d\u7f51\u7edc\u3002

wifiReconnect()\n
"},{"location":"qsl4a/connectivity/wifi/#wifireassociate","title":"wifiReassociate()","text":"

\u91cd\u65b0\u5173\u8054\u5f53\u524d\u63a5\u5165\u70b9\u3002

wifiReassociate()\n
"},{"location":"qsl4a/connectivity/wifi/#_3","title":"\u70ed\u70b9","text":""},{"location":"qsl4a/connectivity/wifi/#wifigetapstate","title":"wifiGetApState()","text":"

\u83b7\u53d6 WiFi AP\uff08\u70ed\u70b9\uff09\u72b6\u6001\u3002

wifiGetApState()\n

\u8fd4\u56de\uff1a \u70ed\u70b9\u72b6\u6001

"},{"location":"qsl4a/connectivity/wifi/#wifi","title":"WiFi \u9501","text":""},{"location":"qsl4a/connectivity/wifi/#wifilockacquirefull","title":"wifiLockAcquireFull()","text":"

\u83b7\u53d6\u5b8c\u6574 WiFi \u9501\uff08\u5373\u4f7f\u5c4f\u5e55\u5173\u95ed\u4e5f\u4fdd\u6301 WiFi \u6d3b\u52a8\uff09\u3002

wifiLockAcquireFull()\n
"},{"location":"qsl4a/connectivity/wifi/#wifilockacquirescanonly","title":"wifiLockAcquireScanOnly()","text":"

\u83b7\u53d6\u4ec5\u626b\u63cf WiFi \u9501\u3002

wifiLockAcquireScanOnly()\n
"},{"location":"qsl4a/connectivity/wifi/#wifilockrelease","title":"wifiLockRelease()","text":"

\u91ca\u653e WiFi \u9501\u3002

wifiLockRelease()\n
"},{"location":"qsl4a/connectivity/wifi/#_4","title":"\u4f7f\u7528\u793a\u4f8b","text":"
import androidhelper\nimport time\n\ndroid = androidhelper.Android()\n\n# \u68c0\u67e5 WiFi \u72b6\u6001\nif droid.checkWifiState().result:\n    print(\"WiFi is enabled\")\nelse:\n    print(\"Enabling WiFi...\")\n    droid.toggleWifiState(True)\n    time.sleep(2)\n\n# \u5f00\u59cb\u626b\u63cf\ndroid.wifiStartScan()\ntime.sleep(3)\n\n# \u83b7\u53d6\u626b\u63cf\u7ed3\u679c\nnetworks = droid.wifiGetScanResults().result\nfor network in networks:\n    print(f\"SSID: {network.get('SSID')}, Signal: {network.get('level')} dBm\")\n\n# \u83b7\u53d6\u8fde\u63a5\u4fe1\u606f\ninfo = droid.wifiGetConnectionInfo().result\nif info:\n    print(f\"Connected to: {info.get('ssid')}\")\n    print(f\"BSSID: {info.get('bssid')}\")\n    print(f\"IP: {info.get('ip_address')}\")\n\n# \u83b7\u53d6 DHCP \u4fe1\u606f\ndhcp = droid.getDhcpInfo().result\nif dhcp:\n    print(f\"Gateway: {dhcp.get('gateway')}\")\n    print(f\"DNS: {dhcp.get('dns1')}\")\n\n# \u83b7\u53d6\u7b80\u5316\u7684\u8fde\u63a5\u4fe1\u606f\nconnected = droid.getConnectedInfo().result\nprint(f\"SSID: {connected.get('ssid')}, Signal: {connected.get('level')} dBm\")\n\n# \u65ad\u5f00\u5e76\u91cd\u65b0\u8fde\u63a5\ndroid.wifiDisconnect()\ntime.sleep(1)\ndroid.wifiReconnect()\n\n# \u91cd\u65b0\u5173\u8054\u63a5\u5165\u70b9\ndroid.wifiReassociate()\n\n# \u68c0\u67e5\u70ed\u70b9\u72b6\u6001\nap_state = droid.wifiGetApState().result\nprint(f\"Hotspot state: {ap_state}\")\n\n# \u83b7\u53d6 WiFi \u9501\u4ee5\u8fdb\u884c\u540e\u53f0\u64cd\u4f5c\ndroid.wifiLockAcquireFull()\n# ... \u6267\u884c\u5de5\u4f5c ...\ndroid.wifiLockRelease()\n\n# \u6216\u4f7f\u7528\u4ec5\u626b\u63cf\u9501\u4ee5\u8fdb\u884c\u8f83\u8f7b\u7684\u540e\u53f0\u64cd\u4f5c\ndroid.wifiLockAcquireScanOnly()\n# ... \u6267\u884c\u626b\u63cf\u5de5\u4f5c ...\ndroid.wifiLockRelease()\n
"},{"location":"qsl4a/core/android-base/","title":"Android \u57fa\u7840\u7c7b","text":"

Android \u7c7b\u662f QSL4A \u7684\u6838\u5fc3\uff0c\u63d0\u4f9b\u4e86\u4e0e Android \u8fd0\u884c\u65f6\u7684\u8fde\u63a5\u548c RPC \u673a\u5236\u3002

"},{"location":"qsl4a/core/android-base/#_1","title":"\u6a21\u5757\u5bfc\u5165","text":"
# \u529f\u80fd\u5b8c\u6574\u7248\u672c\uff08\u63a8\u8350\uff09\nimport androidhelper\ndroid = androidhelper.Android()\n\n# \u7b80\u5316\u7248\u672c\uff08\u5feb\u901f\uff0c\u5355\u5b9e\u4f8b\uff09\nimport android\ndroid = android.droid\n
"},{"location":"qsl4a/core/android-base/#android_1","title":"\u7c7b\uff1aAndroid","text":""},{"location":"qsl4a/core/android-base/#_2","title":"\u6784\u9020\u51fd\u6570","text":"
Android(addr=None)\n

\u53c2\u6570\uff1a - addr (tuple, optional): (HOST, PORT) \u5730\u5740\u3002\u5982\u679c\u4e3a None\uff0c\u5219\u4f7f\u7528\u73af\u5883\u53d8\u91cf AP_HOST \u548c AP_PORT

\u73af\u5883\u53d8\u91cf\uff1a - AP_HOST - \u670d\u52a1\u5668\u4e3b\u673a\u5730\u5740 - AP_PORT - \u670d\u52a1\u5668\u7aef\u53e3 - AP_HANDSHAKE - \u8ba4\u8bc1\u4ee4\u724c

"},{"location":"qsl4a/core/android-base/#_3","title":"\u6838\u5fc3\u65b9\u6cd5","text":""},{"location":"qsl4a/core/android-base/#_rpc","title":"_rpc()","text":"

\u8c03\u7528 Android \u51fd\u6570\u7684\u5185\u90e8 RPC \u65b9\u6cd5\u3002

_rpc(method, *args)\n

\u53c2\u6570\uff1a - method (str): \u8981\u8c03\u7528\u7684\u65b9\u6cd5\u540d - *args: \u65b9\u6cd5\u7684\u53ef\u53d8\u53c2\u6570

\u8fd4\u56de\uff1a Result \u547d\u540d\u5143\u7ec4\uff0c\u5305\u542b\u4ee5\u4e0b\u5b57\u6bb5\uff1a - id (int): \u8bf7\u6c42 ID - result: \u65b9\u6cd5\u8fd4\u56de\u503c - error (str or None): \u9519\u8bef\u4fe1\u606f\uff08\u5982\u679c\u5931\u8d25\uff09

"},{"location":"qsl4a/core/android-base/#getattr","title":"getattr()","text":"

\u52a8\u6001\u65b9\u6cd5\u5206\u53d1\u5668\u3002\u6240\u6709 Android \u65b9\u6cd5\u90fd\u901a\u8fc7\u5c5e\u6027\u67e5\u627e\u8bbf\u95ee\u3002

# \u4ee5\u4e0b\u4e24\u79cd\u65b9\u5f0f\u7b49\u4ef7\uff1a\ndroid.makeToast(\"Hello\")\ndroid._rpc(\"makeToast\", \"Hello\")\n
"},{"location":"qsl4a/core/android-base/#androidpy","title":"\u72ec\u7acb\u51fd\u6570\uff08android.py\uff09","text":"

\u4f7f\u7528\u7b80\u5316\u7684 android \u6a21\u5757\u65f6\uff1a

"},{"location":"qsl4a/core/android-base/#jsla","title":"jsla()","text":"

\u53d1\u9001 JSON-RPC \u8bf7\u6c42\u5e76\u8fd4\u56de\u539f\u59cb\u54cd\u5e94\u3002

jsla(method, *params)\n

\u8fd4\u56de\uff1a JSON \u5b57\u7b26\u4e32\u54cd\u5e94

"},{"location":"qsl4a/core/android-base/#rsla","title":"rsla()","text":"

\u53d1\u9001\u8bf7\u6c42\u5e76\u4ec5\u8fd4\u56de\u7ed3\u679c\u3002

rsla(method, *params)\n

\u8fd4\u56de\uff1a RPC \u8c03\u7528\u7684\u7ed3\u679c\u503c

"},{"location":"qsl4a/core/android-base/#esla","title":"esla()","text":"

\u53d1\u9001\u8bf7\u6c42\uff0c\u9519\u8bef\u65f6\u629b\u51fa\u5f02\u5e38\u3002

esla(method, *params)\n

\u629b\u51fa\uff1a \u5982\u679c error \u5b57\u6bb5\u4e0d\u4e3a None \u5219\u629b\u51fa\u5f02\u5e38

"},{"location":"qsl4a/core/android-base/#nsla","title":"nsla()","text":"

\u53d1\u9001\u8bf7\u6c42\u5e76\u8fd4\u56de Result \u547d\u540d\u5143\u7ec4\u3002

nsla(method, *params)\n

\u8fd4\u56de\uff1a Result \u547d\u540d\u5143\u7ec4

"},{"location":"qsl4a/core/android-base/#_4","title":"\u4f7f\u7528\u793a\u4f8b","text":""},{"location":"qsl4a/core/android-base/#_5","title":"\u57fa\u672c\u8fde\u63a5","text":"
import androidhelper\n\n# \u8fde\u63a5\u5230 Android \u8fd0\u884c\u65f6\ndroid = androidhelper.Android()\n\n# \u901a\u8fc7\u663e\u793a toast \u6765\u68c0\u67e5\u8fde\u63a5\ndroid.makeToast(\"Connected!\")\n
"},{"location":"qsl4a/core/android-base/#_6","title":"\u9519\u8bef\u5904\u7406","text":"
result = droid.someMethod()\nif result.error:\n    print(f\"Error: {result.error}\")\nelse:\n    print(f\"Success: {result.result}\")\n
"},{"location":"qsl4a/core/android-base/#rpc","title":"\u76f4\u63a5 RPC \u8c03\u7528","text":"
# \u76f4\u63a5\u8c03\u7528\u4efb\u610f\u65b9\u6cd5\nresult = droid._rpc(\"makeToast\", \"Hello World\")\n
"},{"location":"qsl4a/core/events/","title":"\u4e8b\u4ef6\u7cfb\u7edf","text":"

QSL4A \u63d0\u4f9b\u4e86\u4e00\u4e2a\u4e8b\u4ef6\u7cfb\u7edf\uff0c\u7528\u4e8e\u5904\u7406\u6765\u81ea Android \u7684\u5f02\u6b65\u4e8b\u4ef6\uff0c\u5982\u4f20\u611f\u5668\u66f4\u65b0\u3001\u4f4d\u7f6e\u53d8\u5316\u548c\u81ea\u5b9a\u4e49\u5e7f\u64ad\u3002

"},{"location":"qsl4a/core/events/#_2","title":"\u4e8b\u4ef6\u57fa\u7840","text":"

\u4e8b\u4ef6\u5b58\u50a8\u5728\u7f13\u51b2\u533a\u4e2d\uff0c\u53ef\u4ee5\u8f6e\u8be2\u6216\u7b49\u5f85\u3002\u6bcf\u4e2a\u4e8b\u4ef6\u5305\u542b\uff1a - name: \u4e8b\u4ef6\u7c7b\u578b/\u540d\u79f0 - data: \u4e8b\u4ef6\u8d1f\u8f7d\u6570\u636e - time: \u65f6\u95f4\u6233

"},{"location":"qsl4a/core/events/#_3","title":"\u4e8b\u4ef6\u65b9\u6cd5","text":""},{"location":"qsl4a/core/events/#eventclearbuffer","title":"eventClearBuffer()","text":"

\u6e05\u9664\u7f13\u51b2\u533a\u4e2d\u7684\u6240\u6709\u5f85\u5904\u7406\u4e8b\u4ef6\u3002

eventClearBuffer()\n

\u8fd4\u56de\uff1a None

"},{"location":"qsl4a/core/events/#eventpoll","title":"eventPoll()","text":"

\u8f6e\u8be2\u7f13\u51b2\u533a\u4e2d\u7684\u4e8b\u4ef6\u3002

eventPoll(number_of_events=1)\n

\u53c2\u6570\uff1a - number_of_events (int): \u8981\u68c0\u7d22\u7684\u6700\u5927\u4e8b\u4ef6\u6570\uff08\u9ed8\u8ba4\uff1a1\uff09

\u8fd4\u56de\uff1a \u4e8b\u4ef6\u5bf9\u8c61\u5217\u8868

"},{"location":"qsl4a/core/events/#eventwait","title":"eventWait()","text":"

\u7b49\u5f85\u4efb\u610f\u4e8b\u4ef6\u3002

eventWait(timeout=None)\n

\u53c2\u6570\uff1a - timeout (int, optional): \u8d85\u65f6\u65f6\u95f4\uff08\u79d2\uff09\u3002None = \u6c38\u4e45\u7b49\u5f85

\u8fd4\u56de\uff1a \u4e8b\u4ef6\u5bf9\u8c61\uff0c\u5982\u679c\u8d85\u65f6\u5219\u8fd4\u56de None

"},{"location":"qsl4a/core/events/#eventwaitfor","title":"eventWaitFor()","text":"

\u7b49\u5f85\u7279\u5b9a\u4e8b\u4ef6\u3002

eventWaitFor(eventName, timeout=None)\n

\u53c2\u6570\uff1a - eventName (str): \u8981\u7b49\u5f85\u7684\u4e8b\u4ef6\u540d\u79f0 - timeout (int, optional): \u8d85\u65f6\u65f6\u95f4\uff08\u79d2\uff09

\u8fd4\u56de\uff1a \u4e8b\u4ef6\u5bf9\u8c61\uff0c\u5982\u679c\u8d85\u65f6\u5219\u8fd4\u56de None

"},{"location":"qsl4a/core/events/#eventpost","title":"eventPost()","text":"

\u53d1\u5e03\u81ea\u5b9a\u4e49\u4e8b\u4ef6\u3002

eventPost(name, data, enqueue=None)\n

\u53c2\u6570\uff1a - name (str): \u4e8b\u4ef6\u540d\u79f0 - data: \u4e8b\u4ef6\u6570\u636e\uff08\u4efb\u610f\u7c7b\u578b\uff09 - enqueue (bool, optional): \u5982\u679c\u4e3a True \u5219\u6dfb\u52a0\u5230\u961f\u5217

"},{"location":"qsl4a/core/events/#receiveevent","title":"receiveEvent()","text":"

\u63a5\u6536\u4e8b\u4ef6\uff08\u963b\u585e\uff09\u3002

receiveEvent()\n

\u8fd4\u56de\uff1a \u4e8b\u4ef6\u5bf9\u8c61

"},{"location":"qsl4a/core/events/#_4","title":"\u5e7f\u64ad\u4e8b\u4ef6","text":"

\u6ce8\u518c\u7cfb\u7edf\u5e7f\u64ad\u4e8b\u4ef6\u3002

"},{"location":"qsl4a/core/events/#eventregisterforbroadcast","title":"eventRegisterForBroadcast()","text":"

\u6ce8\u518c\u63a5\u6536\u5e7f\u64ad\u4e8b\u4ef6\u3002

eventRegisterForBroadcast(category, enqueue=True)\n

\u53c2\u6570\uff1a - category (str): \u5e7f\u64ad\u7c7b\u522b/\u52a8\u4f5c - enqueue (bool): \u6dfb\u52a0\u5230\u4e8b\u4ef6\u961f\u5217

"},{"location":"qsl4a/core/events/#eventunregisterforbroadcast","title":"eventUnregisterForBroadcast()","text":"

\u53d6\u6d88\u6ce8\u518c\u5e7f\u64ad\u4e8b\u4ef6\u3002

eventUnregisterForBroadcast(category)\n
"},{"location":"qsl4a/core/events/#eventgetbrodcastcategories","title":"eventGetBrodcastCategories()","text":"

\u83b7\u53d6\u5df2\u6ce8\u518c\u7684\u5e7f\u64ad\u7c7b\u522b\u3002

eventGetBrodcastCategories()\n

\u8fd4\u56de\uff1a \u5df2\u6ce8\u518c\u7c7b\u522b\u5217\u8868

"},{"location":"qsl4a/core/events/#_5","title":"\u4e8b\u4ef6\u5206\u53d1\u5668)","text":""},{"location":"qsl4a/core/events/#starteventdispatcher","title":"startEventDispatcher()","text":"

\u6253\u5f00\u4e00\u4e2a\u53ef\u4ee5\u8bfb\u53d6\u5df2\u53d1\u5e03\u4e8b\u4ef6\u7684\u5957\u63a5\u5b57\u3002)

startEventDispatcher(port=0)\n

\u53c2\u6570\uff1a - port (int, optional): \u76d1\u542c\u7684\u7aef\u53e3\uff08\u9ed8\u8ba4\uff1a0 = \u81ea\u52a8\u9009\u62e9\uff09

\u8fd4\u56de\uff1a \u6b63\u5728\u76d1\u542c\u7684\u7aef\u53e3\u53f7

"},{"location":"qsl4a/core/events/#stopeventdispatcher","title":"stopEventDispatcher()","text":"

\u505c\u6b62\u4e8b\u4ef6\u670d\u52a1\u5668\u3002)

stopEventDispatcher()\n
"},{"location":"qsl4a/core/events/#_6","title":"\u5df2\u5e9f\u5f03\u65b9\u6cd5","text":""},{"location":"qsl4a/core/events/#rpcpostevent","title":"rpcPostEvent()","text":"

\u5c06\u4e8b\u4ef6\u53d1\u5e03\u5230\u4e8b\u4ef6\u961f\u5217\u3002\uff08\u5df2\u5e9f\u5f03\uff0c\u8bf7\u4f7f\u7528 eventPost\uff09

rpcPostEvent(name, data)\n

\u53c2\u6570\uff1a - name (str): \u4e8b\u4ef6\u540d\u79f0 - data: \u4e8b\u4ef6\u6570\u636e

"},{"location":"qsl4a/core/events/#_7","title":"\u4f7f\u7528\u793a\u4f8b","text":""},{"location":"qsl4a/core/events/#_8","title":"\u57fa\u672c\u4e8b\u4ef6\u8f6e\u8be2","text":"
import androidhelper\n\ndroid = androidhelper.Android()\n\n# \u6e05\u9664\u65e7\u4e8b\u4ef6\ndroid.eventClearBuffer()\n\n# \u8f6e\u8be2\u4e8b\u4ef6\nevents = droid.eventPoll(5).result\nfor event in events:\n    print(f\"Event: {event['name']}, Data: {event['data']}\")\n
"},{"location":"qsl4a/core/events/#_9","title":"\u7b49\u5f85\u4e8b\u4ef6","text":"
# \u7b49\u5f85\u4efb\u610f\u4e8b\u4ef6\uff0c\u5e26\u8d85\u65f6\nevent = droid.eventWait(timeout=10).result\nif event:\n    print(f\"Got event: {event['name']}\")\n
"},{"location":"qsl4a/core/events/#_10","title":"\u7b49\u5f85\u7279\u5b9a\u4e8b\u4ef6","text":"
# \u7b49\u5f85\u4f20\u611f\u5668\u4e8b\u4ef6\nevent = droid.eventWaitFor('screen', timeout=5).result\nif event:\n    print(f\"Screen event: {event['data']}\")\n
"},{"location":"qsl4a/core/events/#_11","title":"\u53d1\u5e03\u81ea\u5b9a\u4e49\u4e8b\u4ef6","text":"
# \u53d1\u5e03\u81ea\u5b9a\u4e49\u4e8b\u4ef6\ndroid.eventPost('my_event', {'key': 'value'})\n\n# \u7b49\u5f85\u5b83\nevent = droid.eventWaitFor('my_event', timeout=1).result\n
"},{"location":"qsl4a/core/events/#_12","title":"\u5e7f\u64ad\u63a5\u6536\u5668","text":"
# \u6ce8\u518c\u5c4f\u5e55\u5f00\u5173\u4e8b\u4ef6\ndroid.eventRegisterForBroadcast('android.intent.action.SCREEN_ON')\ndroid.eventRegisterForBroadcast('android.intent.action.SCREEN_OFF')\n\n# \u7b49\u5f85\u5c4f\u5e55\u4e8b\u4ef6\nwhile True:\n    event = droid.receiveEvent().result\n    if event['name'] == 'android.intent.action.SCREEN_ON':\n        print(\"Screen turned on\")\n    elif event['name'] == 'android.intent.action.SCREEN_OFF':\n        print(\"Screen turned off\")\n
"},{"location":"qsl4a/core/events/#_13","title":"\u4f20\u611f\u5668\u4e8b\u4ef6\u5904\u7406","text":"
# \u5f00\u59cb\u611f\u77e5\ndroid.startSensingTimed(1, 250)\n\n# \u5904\u7406\u4f20\u611f\u5668\u4e8b\u4ef6\nfor _ in range(100):\n    event = droid.eventWait(timeout=1).result\n    if event and event['name'] == 'sensors':\n        data = event['data']\n        print(f\"Accel: {data['xforce']}, {data['yforce']}, {data['zforce']}\")\n\ndroid.stopSensing()\n
"},{"location":"qsl4a/core/events/#_14","title":"\u5e38\u89c1\u4e8b\u4ef6\u7c7b\u578b","text":"\u4e8b\u4ef6\u540d\u79f0 \u63cf\u8ff0 \u6765\u6e90 sensors \u4f20\u611f\u5668\u6570\u636e\u66f4\u65b0 startSensing* location GPS \u4f4d\u7f6e\u66f4\u65b0 startLocating phone \u7535\u8bdd\u72b6\u6001\u53d8\u5316 startTrackingPhoneState signal \u4fe1\u53f7\u5f3a\u5ea6\u53d8\u5316 startTrackingSignalStrength screen \u622a\u56fe\u5c31\u7eea fullGetScreenShot dialog \u5bf9\u8bdd\u6846\u54cd\u5e94 dialogShow*"},{"location":"qsl4a/core/events/#_15","title":"\u4e8b\u4ef6\u6570\u636e\u7ed3\u6784","text":"
{\n    'name': 'event_name',\n    'data': {\n        # \u4e8b\u4ef6\u7279\u5b9a\u6570\u636e\n    },\n    'time': 1234567890  # \u65f6\u95f4\u6233\n}\n
"},{"location":"qsl4a/core/intent/","title":"Intent \u7cfb\u7edf","text":"

Android Intent \u7528\u4e8e\u542f\u52a8\u6d3b\u52a8\u3001\u53d1\u9001\u5e7f\u64ad\u548c\u5e94\u7528\u95f4\u901a\u4fe1\u3002QSL4A \u901a\u8fc7 Intent \u6a21\u5757\u63d0\u4f9b\u5b8c\u6574\u7684 Intent \u652f\u6301\u3002

"},{"location":"qsl4a/core/intent/#_1","title":"\u6a21\u5757\u5bfc\u5165","text":"
import androidhelper\ndon = androidhelper.Android()\n
"},{"location":"qsl4a/core/intent/#intent_1","title":"Intent \u5e38\u91cf","text":"

\u901a\u8fc7 droid.Intent \u8bbf\u95ee\uff1a

"},{"location":"qsl4a/core/intent/#_2","title":"\u64cd\u4f5c","text":"\u5e38\u91cf \u503c \u7528\u9014 ACTION_MAIN android.intent.action.MAIN \u5e94\u7528\u5165\u53e3\u70b9 ACTION_VIEW android.intent.action.VIEW \u67e5\u770b\u5185\u5bb9 ACTION_EDIT android.intent.action.EDIT \u7f16\u8f91\u5185\u5bb9 ACTION_PICK android.intent.action.PICK \u9009\u62e9\u9879\u76ee ACTION_SEND android.intent.action.SEND \u5206\u4eab\u5185\u5bb9 ACTION_SEARCH android.intent.action.SEARCH \u641c\u7d22"},{"location":"qsl4a/core/intent/#_3","title":"\u6807\u5fd7","text":"\u5e38\u91cf \u503c \u7528\u9014 FLAG_ACTIVITY_NEW_TASK 268435456 \u542f\u52a8\u65b0\u4efb\u52a1 FLAG_ACTIVITY_CLEAR_TASK 32768 \u6e05\u9664\u4efb\u52a1\u5806\u6808 FLAG_ACTIVITY_NEW_DOCUMENT 524288 \u65b0\u6587\u6863\u6a21\u5f0f"},{"location":"qsl4a/core/intent/#_4","title":"\u9644\u52a0\u6570\u636e","text":"\u5e38\u91cf \u7528\u9014 EXTRA_TEXT \u6587\u672c\u5185\u5bb9 EXTRA_STREAM \u6587\u4ef6 URI EXTRA_SUBJECT \u4e3b\u9898\u884c EXTRA_EMAIL \u7535\u5b50\u90ae\u4ef6\u5730\u5740"},{"location":"qsl4a/core/intent/#_5","title":"\u6838\u5fc3\u65b9\u6cd5","text":""},{"location":"qsl4a/core/intent/#makeintent","title":"makeIntent()","text":"

\u521b\u5efa Intent \u5bf9\u8c61\u3002

makeIntent(action, uri=None, type=None, extras=None, categories=None,\n           packagename=None, classname=None, flags=None)\n

\u53c2\u6570\uff1a - action (str): Intent \u64cd\u4f5c\uff08\u4f8b\u5982 droid.Intent.ACTION_VIEW\uff09 - uri (str, optional): \u6570\u636e URI - type (str, optional): MIME \u7c7b\u578b - extras (dict, optional): \u9644\u52a0\u6570\u636e - categories (list, optional): Intent \u7c7b\u522b - packagename (str, optional): \u76ee\u6807\u5305 - classname (str, optional): \u76ee\u6807\u7c7b - flags (int, optional): Intent \u6807\u5fd7

\u8fd4\u56de\uff1a Intent \u5bf9\u8c61

"},{"location":"qsl4a/core/intent/#startactivityintent","title":"startActivityIntent()","text":"

\u4f7f\u7528 Intent \u542f\u52a8 Activity\u3002

startActivityIntent(intent, wait=None)\n

\u53c2\u6570\uff1a - intent: makeIntent() \u8fd4\u56de\u7684 Intent \u5bf9\u8c61 - wait (bool, optional): \u963b\u585e\u76f4\u5230\u6d3b\u52a8\u5173\u95ed

"},{"location":"qsl4a/core/intent/#startactivityforresultintent","title":"startActivityForResultIntent()","text":"

\u542f\u52a8\u6d3b\u52a8\u5e76\u7b49\u5f85\u7ed3\u679c\u3002

startActivityForResultIntent(intent)\n

\u8fd4\u56de\uff1a Activity \u7ed3\u679c

"},{"location":"qsl4a/core/intent/#sendbroadcastintent","title":"sendBroadcastIntent()","text":"

\u53d1\u9001\u5e7f\u64ad\u3002

sendBroadcastIntent(intent)\n
"},{"location":"qsl4a/core/intent/#view","title":"view()","text":"

\u901a\u8fc7 URI \u67e5\u770b\u5185\u5bb9\u3002

view(uri, type=None, extras=None)\n
"},{"location":"qsl4a/core/intent/#pick","title":"pick()","text":"

\u4ece URI \u9009\u62e9\u5185\u5bb9\u3002

pick(uri)\n
"},{"location":"qsl4a/core/intent/#intent_2","title":"\u5e38\u89c1 Intent \u65b9\u6cd5)","text":""},{"location":"qsl4a/core/intent/#scanbarcode","title":"scanBarcode()","text":"

\u542f\u52a8\u6761\u7801\u626b\u63cf\u5668\u3002

scanBarcode()\n

\u8fd4\u56de\uff1a \u626b\u63cf\u7684\u6761\u7801\u5b57\u7b26\u4e32

"},{"location":"qsl4a/core/intent/#send","title":"send()","text":"

\u901a\u8fc7\u5206\u4eab Intent \u53d1\u9001\u5185\u5bb9\u3002

send(type, content)\n

\u53c2\u6570\uff1a - type (str): MIME \u7c7b\u578b - content (str): \u8981\u5206\u4eab\u7684\u5185\u5bb9

"},{"location":"qsl4a/core/intent/#sendtext","title":"sendText()","text":"

\u53d1\u9001\u6587\u672c\u5185\u5bb9\u3002

sendText(text)\n

\u53c2\u6570\uff1a - text (str): \u8981\u53d1\u9001\u7684\u6587\u672c

"},{"location":"qsl4a/core/intent/#sendemail","title":"sendEmail()","text":"

\u53d1\u9001\u7535\u5b50\u90ae\u4ef6\u3002

sendEmail(to, subject, body, attachment=None)\n

\u53c2\u6570\uff1a - to (str or list): \u6536\u4ef6\u4eba\u7535\u5b50\u90ae\u4ef6\u5730\u5740 - subject (str): \u7535\u5b50\u90ae\u4ef6\u4e3b\u9898 - body (str): \u7535\u5b50\u90ae\u4ef6\u6b63\u6587 - attachment (str, optional): \u9644\u4ef6\u6587\u4ef6\u8def\u5f84

"},{"location":"qsl4a/core/intent/#pathtouri","title":"pathToUri()","text":"

\u5c06\u6587\u4ef6\u8def\u5f84\u8f6c\u6362\u4e3a\u5185\u5bb9 URI\u3002

pathToUri(path)\n

\u53c2\u6570\uff1a - path (str): \u6587\u4ef6\u8def\u5f84

\u8fd4\u56de\uff1a \u5185\u5bb9 URI \u5b57\u7b26\u4e32

"},{"location":"qsl4a/core/intent/#openfile","title":"openFile()","text":"

\u7528\u9002\u5f53\u7684\u5e94\u7528\u7a0b\u5e8f\u6253\u5f00\u6587\u4ef6\u3002

openFile(path)\n

\u53c2\u6570\uff1a - path (str): \u8981\u6253\u5f00\u7684\u6587\u4ef6\u8def\u5f84

"},{"location":"qsl4a/core/intent/#sendfile","title":"sendFile()","text":"

\u901a\u8fc7\u5206\u4eab Intent \u53d1\u9001\u6587\u4ef6\u3002

sendFile(path)\n

\u53c2\u6570\uff1a - path (str): \u8981\u53d1\u9001\u7684\u6587\u4ef6\u8def\u5f84

"},{"location":"qsl4a/core/intent/#getpathtype","title":"getPathType()","text":"

\u83b7\u53d6\u6587\u4ef6\u8def\u5f84\u7684 MIME \u7c7b\u578b\u3002

getPathType(path)\n

\u53c2\u6570\uff1a - path (str): \u6587\u4ef6\u8def\u5f84

\u8fd4\u56de\uff1a MIME \u7c7b\u578b\u5b57\u7b26\u4e32

"},{"location":"qsl4a/core/intent/#viewmap","title":"viewMap()","text":"

\u5728\u6307\u5b9a\u4f4d\u7f6e\u6253\u5f00\u5730\u56fe\u3002

viewMap(latitude, longitude)\n

\u53c2\u6570\uff1a - latitude (float): \u7eac\u5ea6 - longitude (float): \u7ecf\u5ea6

"},{"location":"qsl4a/core/intent/#viewcontacts","title":"viewContacts()","text":"

\u6253\u5f00\u8054\u7cfb\u4eba\u5e94\u7528\u3002

viewContacts()\n
"},{"location":"qsl4a/core/intent/#search","title":"search()","text":"

\u6267\u884c\u7f51\u7edc\u641c\u7d22\u3002

search(query)\n

\u53c2\u6570\uff1a - query (str): \u641c\u7d22\u67e5\u8be2

"},{"location":"qsl4a/core/intent/#viewhtml","title":"viewHtml()","text":"

\u67e5\u770b HTML \u5185\u5bb9\u3002

viewHtml(content, encoding=None)\n

\u53c2\u6570\uff1a - content (str): HTML \u5185\u5bb9 - encoding (str, optional): \u5b57\u7b26\u7f16\u7801

"},{"location":"qsl4a/core/intent/#webviewshow","title":"webViewShow()","text":"

\u5728 WebView \u4e2d\u663e\u793a\u7f51\u9875\u5185\u5bb9\u3002\u5df2\u5e9f\u5f03\uff0c\u8bf7\u4f7f\u7528 viewHtml\u3002

webViewShow(url)\n

\u53c2\u6570\uff1a - url (str): \u7f51\u9875 URL

"},{"location":"qsl4a/core/intent/#editoropen","title":"editorOpen()","text":"

\u6253\u5f00\u6587\u672c\u7f16\u8f91\u5668\u3002

editorOpen(path=None, create=False)\n

\u53c2\u6570\uff1a - path (str, optional): \u8981\u7f16\u8f91\u7684\u6587\u4ef6\u8def\u5f84 - create (bool, optional): \u5982\u679c\u4e0d\u5b58\u5728\u5219\u521b\u5efa

"},{"location":"qsl4a/core/intent/#uri","title":"\u8f85\u52a9\u7c7b\uff1aUri","text":"

\u521b\u5efa Intent \u7684 URI \u5bf9\u8c61\uff1a

from androidhelper.Intent import Uri\n\nuri = Uri(\"file:///sdcard/test.txt\")\n
"},{"location":"qsl4a/core/intent/#_6","title":"\u4f7f\u7528\u793a\u4f8b","text":""},{"location":"qsl4a/core/intent/#_7","title":"\u6253\u5f00\u7f51\u9875","text":"
intent = droid.makeIntent(\n    action=droid.Intent.ACTION_VIEW,\n    uri=\"http://www.example.com\"\n).result\ndroid.startActivityIntent(intent)\n
"},{"location":"qsl4a/core/intent/#_8","title":"\u5206\u4eab\u6587\u672c","text":"
intent = droid.makeIntent(\n    action=droid.Intent.ACTION_SEND,\n    extras={\n        droid.Intent.EXTRA_TEXT: \"Hello from QPython!\",\n        droid.Intent.EXTRA_SUBJECT: \"Test\"\n    },\n    type=\"text/plain\"\n).result\ndroid.startActivityIntent(intent)\n
"},{"location":"qsl4a/core/intent/#_9","title":"\u6253\u5f00\u6587\u4ef6","text":"
intent = droid.makeIntent(\n    action=droid.Intent.ACTION_VIEW,\n    uri=\"file:///sdcard/document.pdf\",\n    type=\"application/pdf\"\n).result\ndroid.startActivityIntent(intent)\n
"},{"location":"qsl4a/core/intent/#_10","title":"\u9009\u62e9\u8054\u7cfb\u4eba","text":"
result = droid.pickContact()\ncontact_uri = result.result\n
"},{"location":"qsl4a/core/intent/#_11","title":"\u53d1\u9001\u7535\u5b50\u90ae\u4ef6","text":"
intent = droid.makeIntent(\n    action=droid.Intent.ACTION_SEND,\n    extras={\n        droid.Intent.EXTRA_EMAIL: [\"test@example.com\"],\n        droid.Intent.EXTRA_SUBJECT: \"Hello\",\n        droid.Intent.EXTRA_TEXT: \"Message body\"\n    },\n    type=\"message/rfc822\"\n).result\ndroid.startActivityIntent(intent)\n
"},{"location":"qsl4a/core/intent/#_12","title":"\u6253\u5f00\u5e94\u7528","text":"
intent = droid.makeIntent(\n    action=droid.Intent.ACTION_MAIN,\n    packagename=\"com.android.settings\",\n    classname=\"com.android.settings.Settings\"\n).result\ndroid.startActivityIntent(intent)\n
"},{"location":"qsl4a/hardware/bluetooth/","title":"\u84dd\u7259 API","text":"

\u63a7\u5236\u84dd\u7259\u9002\u914d\u5668\u5e76\u4e0e\u84dd\u7259\u8bbe\u5907\u901a\u4fe1\u3002

"},{"location":"qsl4a/hardware/bluetooth/#_1","title":"\u9002\u914d\u5668\u63a7\u5236","text":""},{"location":"qsl4a/hardware/bluetooth/#togglebluetoothstate","title":"toggleBluetoothState()","text":"

\u6253\u5f00/\u5173\u95ed\u84dd\u7259\u3002

toggleBluetoothState(enabled=None, prompt=True)\n

\u53c2\u6570\uff1a - enabled (bool): True=\u5f00\uff0cFalse=\u5173\uff0cNone=\u5207\u6362 - prompt (bool): \u663e\u793a\u7528\u6237\u63d0\u793a

"},{"location":"qsl4a/hardware/bluetooth/#checkbluetoothstate","title":"checkBluetoothState()","text":"

\u68c0\u67e5\u84dd\u7259\u662f\u5426\u5df2\u542f\u7528\u3002

checkBluetoothState()\n

\u8fd4\u56de\uff1a True/False

"},{"location":"qsl4a/hardware/bluetooth/#getlocalname","title":"GetLocalName()","text":"

\u83b7\u53d6\u84dd\u7259\u8bbe\u5907\u540d\u79f0\u3002

GetLocalName()\n
"},{"location":"qsl4a/hardware/bluetooth/#setlocalname","title":"SetLocalName()","text":"

\u8bbe\u7f6e\u84dd\u7259\u8bbe\u5907\u540d\u79f0\u3002

SetLocalName(name)\n
"},{"location":"qsl4a/hardware/bluetooth/#getscanmode","title":"GetScanMode()","text":"

\u83b7\u53d6\u53ef\u53d1\u73b0\u6027\u6a21\u5f0f\u3002

GetScanMode()\n

\u8fd4\u56de\uff1a - -1: \u5df2\u7981\u7528 - 0: \u4e0d\u53ef\u53d1\u73b0\uff0c\u4e0d\u53ef\u8fde\u63a5 - 1: \u53ef\u8fde\u63a5\uff0c\u4e0d\u53ef\u53d1\u73b0 - 3: \u53ef\u8fde\u63a5\u4e14\u53ef\u53d1\u73b0

"},{"location":"qsl4a/hardware/bluetooth/#makediscoverable","title":"MakeDiscoverable()","text":"

\u4f7f\u8bbe\u5907\u53ef\u53d1\u73b0\u3002

MakeDiscoverable(duration=300)\n

\u53c2\u6570\uff1a - duration (int): \u53ef\u53d1\u73b0\u7684\u79d2\u6570

"},{"location":"qsl4a/hardware/bluetooth/#_2","title":"\u53d1\u73b0","text":""},{"location":"qsl4a/hardware/bluetooth/#discoverystart","title":"DiscoveryStart()","text":"

\u5f00\u59cb\u8bbe\u5907\u53d1\u73b0\u3002

DiscoveryStart()\n
"},{"location":"qsl4a/hardware/bluetooth/#discoverycancel","title":"DiscoveryCancel()","text":"

\u53d6\u6d88\u53d1\u73b0\u3002

DiscoveryCancel()\n
"},{"location":"qsl4a/hardware/bluetooth/#getreceiveddevices","title":"GetReceivedDevices()","text":"

\u83b7\u53d6\u53d1\u73b0\u7684\u8bbe\u5907\u3002

GetReceivedDevices()\n

\u8fd4\u56de\uff1a \u8bbe\u5907\u4fe1\u606f\u5b57\u5178\u5217\u8868

"},{"location":"qsl4a/hardware/bluetooth/#getbondeddevices","title":"GetBondedDevices()","text":"

\u83b7\u53d6\u914d\u5bf9\u8bbe\u5907\u3002

GetBondedDevices()\n

\u8fd4\u56de\uff1a \u914d\u5bf9\u8bbe\u5907\u4fe1\u606f\u5217\u8868

"},{"location":"qsl4a/hardware/bluetooth/#_3","title":"\u8fde\u63a5","text":""},{"location":"qsl4a/hardware/bluetooth/#connect","title":"Connect()","text":"

\u8fde\u63a5\u5230\u8bbe\u5907\u3002

Connect(uuid=\"457807c0-4897-11df-9879-0800200c9a66\", address=None)\n

\u53c2\u6570\uff1a - uuid (str): \u670d\u52a1 UUID - address (str): \u8bbe\u5907\u5730\u5740\uff08None = \u663e\u793a\u9009\u62e9\u5668\uff09

\u8fd4\u56de\uff1a \u5982\u679c\u6210\u529f\u5219\u4e3a True

"},{"location":"qsl4a/hardware/bluetooth/#accept","title":"Accept()","text":"

\u63a5\u53d7\u4f20\u5165\u8fde\u63a5\u3002

Accept(uuid=\"457807c0-4897-11df-9879-0800200c9a66\", timeout=0)\n
"},{"location":"qsl4a/hardware/bluetooth/#activeconnections","title":"ActiveConnections()","text":"

\u68c0\u67e5\u6d3b\u52a8\u8fde\u63a5\u3002

ActiveConnections()\n
"},{"location":"qsl4a/hardware/bluetooth/#stop","title":"Stop()","text":"

\u65ad\u5f00\u8fde\u63a5\u3002

Stop(connID=None)\n
"},{"location":"qsl4a/hardware/bluetooth/#_4","title":"\u901a\u4fe1","text":""},{"location":"qsl4a/hardware/bluetooth/#write","title":"Write()","text":"

\u53d1\u9001 ASCII \u6570\u636e\u3002

Write(ascii, connID=\"\")\n
"},{"location":"qsl4a/hardware/bluetooth/#writebinary","title":"WriteBinary()","text":"

\u53d1\u9001\u4e8c\u8fdb\u5236\u6570\u636e\uff08base64 \u7f16\u7801\uff09\u3002

WriteBinary(base64, connID=None)\n
"},{"location":"qsl4a/hardware/bluetooth/#read","title":"Read()","text":"

\u8bfb\u53d6 ASCII \u6570\u636e\u3002

Read(bufferSize=4096, connID=None)\n
"},{"location":"qsl4a/hardware/bluetooth/#readbinary","title":"ReadBinary()","text":"

\u8bfb\u53d6\u4e8c\u8fdb\u5236\u6570\u636e\u3002

ReadBinary(bufferSize=4096, connID=None)\n
"},{"location":"qsl4a/hardware/bluetooth/#readline","title":"ReadLine()","text":"

\u8bfb\u53d6\u4e00\u884c\u3002

ReadLine(connID=None)\n
"},{"location":"qsl4a/hardware/bluetooth/#readready","title":"ReadReady()","text":"

\u68c0\u67e5\u662f\u5426\u6709\u53ef\u7528\u6570\u636e\u3002

ReadReady(connID=None)\n
"},{"location":"qsl4a/hardware/bluetooth/#_5","title":"\u4f7f\u7528\u793a\u4f8b","text":"
import androidhelper\n\ndroid = androidhelper.Android()\n\n# \u542f\u7528\u84dd\u7259\ndroid.toggleBluetoothState(True)\n\n# \u83b7\u53d6\u914d\u5bf9\u8bbe\u5907\ndevices = droid.GetBondedDevices().result\nfor dev in devices:\n    print(f\"{dev['name']}: {dev['address']}\")\n\n# \u8fde\u63a5\u5230\u8bbe\u5907\ndroid.Connect(address=\"00:11:22:33:44:55\")\n\n# \u53d1\u9001\u6570\u636e\ndroid.Write(\"Hello Bluetooth!\")\n\n# \u8bfb\u53d6\u54cd\u5e94\ndata = droid.Read(1024).result\n
"},{"location":"qsl4a/hardware/camera/","title":"\u76f8\u673a API","text":"

\u62cd\u6444\u7167\u7247\u548c\u5f55\u5236\u89c6\u9891\u3002

"},{"location":"qsl4a/hardware/camera/#_1","title":"\u7167\u7247\u62cd\u6444","text":""},{"location":"qsl4a/hardware/camera/#takepicture","title":"takePicture()","text":"

\u4f7f\u7528\u9ed8\u8ba4\u76f8\u673a\u62cd\u6444\u7167\u7247\u3002

takePicture(path=None)\n

\u53c2\u6570\uff1a - path (str, optional): \u4fdd\u5b58\u8def\u5f84\u3002\u5982\u679c\u4e3a None\uff0c\u5219\u8fd4\u56de\u56fe\u50cf\u6570\u636e

\u8fd4\u56de\uff1a \u56fe\u50cf\u8def\u5f84\u6216\u56fe\u50cf\u6570\u636e

"},{"location":"qsl4a/hardware/camera/#cameracapturepicture","title":"cameraCapturePicture()","text":"

\u4f7f\u7528\u9ad8\u7ea7\u76f8\u673a\u63a7\u5236\u62cd\u6444\u7167\u7247\u3002

cameraCapturePicture(targetPath=None, cameraId=0, useAutoFocus=True)\n

\u53c2\u6570\uff1a - targetPath (str, optional): \u4fdd\u5b58\u8def\u5f84 - cameraId (int): \u4f7f\u7528\u7684\u76f8\u673a\uff080 = \u540e\u7f6e\uff0c1 = \u524d\u7f6e\uff09 - useAutoFocus (bool): \u542f\u7528\u81ea\u52a8\u5bf9\u7126

\u8fd4\u56de\uff1a \u62cd\u6444\u7684\u56fe\u50cf\u8def\u5f84

"},{"location":"qsl4a/hardware/camera/#camerasettorchmode","title":"cameraSetTorchMode()","text":"

\u63a7\u5236\u76f8\u673a\u95ea\u5149\u706f/\u624b\u7535\u7b52\u3002

cameraSetTorchMode(enabled)\n

\u53c2\u6570\uff1a - enabled (bool): True \u6253\u5f00\uff0cFalse \u5173\u95ed

\u8fd4\u56de\uff1a \u5982\u679c\u6210\u529f\u5219\u4e3a True

"},{"location":"qsl4a/hardware/camera/#imagereadergetscreenshot","title":"imageReaderGetScreenShot()","text":"

\u622a\u56fe\u3002

imageReaderGetScreenShot(path=None, delayMilliSec=1000)\n

\u53c2\u6570\uff1a - path (str, optional): \u4fdd\u5b58\u8def\u5f84 - delayMilliSec (int): \u62cd\u6444\u524d\u5ef6\u8fdf

"},{"location":"qsl4a/hardware/camera/#_2","title":"\u89c6\u9891\u5f55\u5236","text":""},{"location":"qsl4a/hardware/camera/#takevideo","title":"takeVideo()","text":"

\u4f7f\u7528\u9ed8\u8ba4\u8bbe\u7f6e\u5f55\u5236\u89c6\u9891\u3002

takeVideo(path=None, quality=1)\n

\u53c2\u6570\uff1a - path (str, optional): \u4fdd\u5b58\u8def\u5f84 - quality (int): \u89c6\u9891\u8d28\u91cf\uff080-4\uff09 - 0: 160x120 - 1: 320x240 - 2: 352x288 - 3: 640x480 - 4: 800x480

"},{"location":"qsl4a/hardware/camera/#recordercapturevideo","title":"recorderCaptureVideo()","text":"

\u4f7f\u7528\u9ad8\u7ea7\u63a7\u5236\u5f55\u5236\u89c6\u9891\u3002

recorderCaptureVideo(targetPath=None, duration=10, cameraId=0, quality=8)\n

\u53c2\u6570\uff1a - targetPath (str, optional): \u4fdd\u5b58\u8def\u5f84 - duration (int): \u5f55\u5236\u65f6\u957f\uff08\u79d2\uff09\uff08\u9ed8\u8ba4\uff1a10\uff09 - cameraId (int): \u4f7f\u7528\u7684\u76f8\u673a\uff080 = \u540e\u7f6e\uff0c1 = \u524d\u7f6e\uff09 - quality (int): \u89c6\u9891\u8d28\u91cf\uff080-8\uff0c\u8d8a\u9ad8\u8d8a\u597d\uff09

\u8fd4\u56de\uff1a \u89c6\u9891\u6587\u4ef6\u8def\u5f84

"},{"location":"qsl4a/hardware/camera/#recordaudio","title":"recordAudio()","text":"

\u5f55\u5236\u97f3\u9891\u3002

recordAudio()\n

\u8fd4\u56de\uff1a \u97f3\u9891\u6587\u4ef6\u8def\u5f84

"},{"location":"qsl4a/hardware/camera/#_3","title":"\u5c4f\u5e55\u5f55\u5236","text":""},{"location":"qsl4a/hardware/camera/#recorderstartscreenrecord","title":"recorderStartScreenRecord()","text":"

\u5f00\u59cb\u5c4f\u5e55\u5f55\u5236\u3002

recorderStartScreenRecord(path=None, audio=1, targetPixels=None,\n                          frameRate=30, bitRate=None, rotation=False,\n                          autoStart=True)\n

\u53c2\u6570\uff1a - path (str): \u4fdd\u5b58\u8def\u5f84 - audio (int): \u97f3\u9891\u6e90\uff080=\u65e0\uff0c1=\u9ea6\u514b\u98ce\uff0c2=\u5185\u90e8\uff09 - targetPixels (int): \u5206\u8fa8\u7387 - frameRate (int): FPS - bitRate (int): \u6bd4\u7279\u7387 - rotation (bool): \u65cb\u8f6c\u8f93\u51fa - autoStart (bool): \u7acb\u5373\u5f00\u59cb

"},{"location":"qsl4a/hardware/camera/#recorderstart","title":"recorderStart()","text":"

\u5f00\u59cb\u5f55\u5236\u3002

recorderStart()\n
"},{"location":"qsl4a/hardware/camera/#recorderpause","title":"recorderPause()","text":"

\u6682\u505c\u5f55\u5236\u3002

recorderPause()\n
"},{"location":"qsl4a/hardware/camera/#recorderresume","title":"recorderResume()","text":"

\u6062\u590d\u5f55\u5236\u3002

recorderResume()\n
"},{"location":"qsl4a/hardware/camera/#_4","title":"\u97f3\u9891\u97f3\u91cf\u68c0\u6d4b","text":""},{"location":"qsl4a/hardware/camera/#recordersoundvolumedetect","title":"recorderSoundVolumeDetect()","text":"

\u5f00\u59cb\u97f3\u91cf\u68c0\u6d4b\u3002

recorderSoundVolumeDetect(interval=100)\n
"},{"location":"qsl4a/hardware/camera/#recordersoundvolumegetdb","title":"recorderSoundVolumeGetDb()","text":"

\u83b7\u53d6\u5f53\u524d\u97f3\u91cf\uff08\u5206\u8d1d\uff09\u3002

recorderSoundVolumeGetDb()\n
"},{"location":"qsl4a/hardware/camera/#_5","title":"\u4f7f\u7528\u793a\u4f8b","text":"
import androidhelper\n\ndroid = androidhelper.Android()\n\n# \u4f7f\u7528\u9ed8\u8ba4\u76f8\u673a\u62cd\u7167\nphoto_path = droid.takePicture(\"/sdcard/photo.jpg\").result\nprint(f\"Photo saved: {photo_path}\")\n\n# \u4f7f\u7528\u524d\u7f6e\u76f8\u673a\u548c\u81ea\u52a8\u5bf9\u7126\u62cd\u7167\ncamera_path = droid.cameraCapturePicture(\"/sdcard/selfie.jpg\", cameraId=1, useAutoFocus=True).result\nprint(f\"Front camera photo: {camera_path}\")\n\n# \u63a7\u5236\u95ea\u5149\u706f\ndroid.cameraSetTorchMode(True)  # \u6253\u5f00\u95ea\u5149\u706f\n\n# \u4f7f\u7528\u9ed8\u8ba4\u8bbe\u7f6e\u5f55\u5236\u89c6\u9891\nvideo_path = droid.takeVideo(\"/sdcard/video.mp4\", quality=3).result\nprint(f\"Video saved: {video_path}\")\n\n# \u4f7f\u7528\u9ad8\u7ea7\u63a7\u5236\u5f55\u5236\u89c6\u9891\nvideo_path = droid.recorderCaptureVideo(\"/sdcard/movie.mp4\", duration=30, cameraId=0, quality=8).result\n\n# \u622a\u56fe\nss_path = droid.imageReaderGetScreenShot(\"/sdcard/screenshot.png\", 500).result\n
"},{"location":"qsl4a/hardware/recorder/","title":"\u97f3\u9891\u5f55\u5236 API","text":"

\u4ece\u9ea6\u514b\u98ce\u548c\u8bbe\u5907\u5c4f\u5e55\u5f55\u5236\u97f3\u9891\u3002

"},{"location":"qsl4a/hardware/recorder/#_1","title":"\u97f3\u9891\u5f55\u5236","text":""},{"location":"qsl4a/hardware/recorder/#recordaudio","title":"recordAudio()","text":"

\u4ece\u9ea6\u514b\u98ce\u5f55\u5236\u97f3\u9891\u3002

recordAudio()\n

\u8fd4\u56de\uff1a \u5f55\u5236\u7684\u97f3\u9891\u6587\u4ef6\u8def\u5f84

"},{"location":"qsl4a/hardware/recorder/#recorderstartmicrophone","title":"recorderStartMicrophone()","text":"

\u5f00\u59cb\u4ece\u9ea6\u514b\u98ce\u5f55\u5236\u5230\u7279\u5b9a\u6587\u4ef6\u3002

recorderStartMicrophone(targetPath=None)\n

\u53c2\u6570\uff1a - targetPath (str, optional): \u4fdd\u5b58\u5f55\u5236\u7684\u8def\u5f84

"},{"location":"qsl4a/hardware/recorder/#_2","title":"\u5c4f\u5e55\u5f55\u5236","text":""},{"location":"qsl4a/hardware/recorder/#recorderstartscreenrecord","title":"recorderStartScreenRecord()","text":"

\u5f00\u59cb\u5e26\u97f3\u9891\u7684\u5c4f\u5e55\u5f55\u5236\u3002

recorderStartScreenRecord(path=None, audio=1, targetPixels=None,\n                          frameRate=30, bitRate=None, rotation=False,\n                          autoStart=True)\n

\u53c2\u6570\uff1a - path (str): \u89c6\u9891\u6587\u4ef6\u4fdd\u5b58\u8def\u5f84 - audio (int): \u97f3\u9891\u6e90\uff080=\u65e0\uff0c1=\u9ea6\u514b\u98ce\uff0c2=\u5185\u90e8\u97f3\u9891\uff09 - targetPixels (int): \u76ee\u6807\u5206\u8fa8\u7387\uff08\u50cf\u7d20\uff09 - frameRate (int): \u6bcf\u79d2\u5e27\u6570\uff08\u9ed8\u8ba4\uff1a30\uff09 - bitRate (int): \u89c6\u9891\u6bd4\u7279\u7387 - rotation (bool): \u65cb\u8f6c\u8f93\u51fa\u89c6\u9891 - autoStart (bool): \u7acb\u5373\u5f00\u59cb\u5f55\u5236

\u8fd4\u56de\uff1a \u64cd\u4f5c\u7ed3\u679c

"},{"location":"qsl4a/hardware/recorder/#recorderstart","title":"recorderStart()","text":"

\u5f00\u59cb\u5c4f\u5e55\u5f55\u5236\uff08\u5f53 autoStart=False \u65f6\uff09\u3002

recorderStart()\n
"},{"location":"qsl4a/hardware/recorder/#recorderpause","title":"recorderPause()","text":"

\u6682\u505c\u6b63\u5728\u8fdb\u884c\u7684\u5c4f\u5e55\u5f55\u5236\u3002

recorderPause()\n
"},{"location":"qsl4a/hardware/recorder/#recorderresume","title":"recorderResume()","text":"

\u6062\u590d\u6682\u505c\u7684\u5c4f\u5e55\u5f55\u5236\u3002

recorderResume()\n
"},{"location":"qsl4a/hardware/recorder/#_3","title":"\u97f3\u9891\u97f3\u91cf\u68c0\u6d4b","text":""},{"location":"qsl4a/hardware/recorder/#recordersoundvolumedetect","title":"recorderSoundVolumeDetect()","text":"

\u5f00\u59cb\u76d1\u63a7\u97f3\u91cf\u7ea7\u522b\u3002

recorderSoundVolumeDetect(interval=100)\n

\u53c2\u6570\uff1a - interval (int): \u68c0\u6d4b\u95f4\u9694\uff08\u6beb\u79d2\uff09\uff08\u9ed8\u8ba4\uff1a100\uff09

"},{"location":"qsl4a/hardware/recorder/#recordersoundvolumegetdb","title":"recorderSoundVolumeGetDb()","text":"

\u83b7\u53d6\u5f53\u524d\u97f3\u91cf\uff08\u5206\u8d1d\uff09\u3002

recorderSoundVolumeGetDb()\n

\u8fd4\u56de\uff1a \u5f53\u524d\u97f3\u91cf\u7ea7\u522b\uff08dB\uff09

"},{"location":"qsl4a/hardware/recorder/#_4","title":"\u4f7f\u7528\u793a\u4f8b","text":"
import androidhelper\nimport time\n\ndroid = androidhelper.Android()\n\n# \u4ece\u9ea6\u514b\u98ce\u5f55\u5236\u97f3\u9891\nprint(\"Recording audio...\")\naudio_path = droid.recordAudio().result\nprint(f\"Saved to: {audio_path}\")\n\n# \u5f55\u5236\u5230\u7279\u5b9a\u6587\u4ef6\ndroid.recorderStartMicrophone(\"/sdcard/my_recording.mp3\")\ntime.sleep(5)\ndroid.recorderStop()\n\n# \u5e26\u97f3\u9891\u5f55\u5236\u5c4f\u5e55\nprint(\"Starting screen recording...\")\ndroid.recorderStartScreenRecord(\n    path=\"/sdcard/screen_record.mp4\",\n    audio=1,\n    frameRate=30,\n    autoStart=True\n)\ntime.sleep(10)\ndroid.recorderStop()\n\n# \u76d1\u63a7\u97f3\u91cf\ndroid.recorderSoundVolumeDetect(interval=100)\ntime.sleep(3)\nvolume = droid.recorderSoundVolumeGetDb().result\nprint(f\"Current volume: {volume} dB\")\n
"},{"location":"qsl4a/hardware/usbserial/","title":"USB \u4e3b\u673a\u4e32\u884c API","text":"

\u4e0e USB \u4e32\u884c\u8bbe\u5907\u901a\u4fe1\uff08\u9700\u8981 USB OTG \u652f\u6301\u548c Android 3.1+\uff09\u3002

"},{"location":"qsl4a/hardware/usbserial/#usb","title":"USB \u4e32\u884c\u65b9\u6cd5)","text":""},{"location":"qsl4a/hardware/usbserial/#usbhostserialopen","title":"usbHostSerialOpen()","text":"

\u6253\u5f00\u4e0e USB \u4e32\u884c\u8bbe\u5907\u7684\u8fde\u63a5\u3002

usbHostSerialOpen(device, baudRate=9600)\n

\u53c2\u6570\uff1a - device (str): USB \u8bbe\u5907\u8def\u5f84\u6216\u6807\u8bc6\u7b26 - baudRate (int): \u6ce2\u7279\u7387\uff08\u9ed8\u8ba4\uff1a9600\uff09

\u8fd4\u56de\uff1a \u5982\u679c\u6253\u5f00\u6210\u529f\u5219\u4e3a True

"},{"location":"qsl4a/hardware/usbserial/#usbhostserialclose","title":"usbHostSerialClose()","text":"

\u5173\u95ed USB \u4e32\u884c\u8fde\u63a5\u3002

usbHostSerialClose()\n
"},{"location":"qsl4a/hardware/usbserial/#usbhostserialread","title":"usbHostSerialRead()","text":"

\u4ece USB \u4e32\u884c\u8bfb\u53d6\u6570\u636e\u3002

usbHostSerialRead(bufferSize=1024)\n

\u53c2\u6570\uff1a - bufferSize (int): \u8981\u8bfb\u53d6\u7684\u6700\u5927\u5b57\u8282\u6570\uff08\u9ed8\u8ba4\uff1a1024\uff09

\u8fd4\u56de\uff1a \u8bfb\u53d6\u7684\u6570\u636e\u5b57\u7b26\u4e32

"},{"location":"qsl4a/hardware/usbserial/#usbhostserialwrite","title":"usbHostSerialWrite()","text":"

\u5411 USB \u4e32\u884c\u5199\u5165\u6570\u636e\u3002

usbHostSerialWrite(data)\n

\u53c2\u6570\uff1a - data (str): \u8981\u5199\u5165\u7684\u5b57\u7b26\u4e32\u6570\u636e

"},{"location":"qsl4a/hardware/usbserial/#usbhostserialavailable","title":"usbHostSerialAvailable()","text":"

\u68c0\u67e5\u662f\u5426\u6709\u53ef\u8bfb\u53d6\u7684\u6570\u636e\u3002

usbHostSerialAvailable()\n

\u8fd4\u56de\uff1a \u53ef\u7528\u5b57\u8282\u6570

"},{"location":"qsl4a/hardware/usbserial/#_1","title":"\u914d\u7f6e\u65b9\u6cd5)","text":""},{"location":"qsl4a/hardware/usbserial/#usbhostserialsetbaudrate","title":"usbHostSerialSetBaudRate()","text":"

\u8bbe\u7f6e\u6ce2\u7279\u7387\u3002

usbHostSerialSetBaudRate(baudRate)\n

\u53c2\u6570\uff1a - baudRate (int): \u6ce2\u7279\u7387

"},{"location":"qsl4a/hardware/usbserial/#usbhostserialsetdatabits","title":"usbHostSerialSetDataBits()","text":"

\u8bbe\u7f6e\u6570\u636e\u4f4d\uff085\u30016\u30017 \u6216 8\uff09\u3002

usbHostSerialSetDataBits(dataBits)\n

\u53c2\u6570\uff1a - dataBits (int): \u6570\u636e\u4f4d\uff085-8\uff09

"},{"location":"qsl4a/hardware/usbserial/#usbhostserialsetstopbits","title":"usbHostSerialSetStopBits()","text":"

\u8bbe\u7f6e\u505c\u6b62\u4f4d\uff081\u30011.5 \u6216 2\uff09\u3002

usbHostSerialSetStopBits(stopBits)\n

\u53c2\u6570\uff1a - stopBits (float): \u505c\u6b62\u4f4d\uff081\u30011.5 \u6216 2\uff09

"},{"location":"qsl4a/hardware/usbserial/#usbhostserialsetparity","title":"usbHostSerialSetParity()","text":"

\u8bbe\u7f6e\u6821\u9a8c\u4f4d\uff08\u65e0\u3001\u5947\u3001\u5076\u3001\u6807\u8bb0\u3001\u7a7a\u683c\uff09\u3002

usbHostSerialSetParity(parity)\n

\u53c2\u6570\uff1a - parity (str): \u6821\u9a8c\u6a21\u5f0f\uff08'none'\u3001'odd'\u3001'even'\u3001'mark'\u3001'space'\uff09

"},{"location":"qsl4a/hardware/usbserial/#usbhostserialsetflowcontrol","title":"usbHostSerialSetFlowControl()","text":"

\u8bbe\u7f6e\u6d41\u63a7\u5236\uff08\u65e0\u3001\u786c\u4ef6\u3001\u8f6f\u4ef6\uff09\u3002

usbHostSerialSetFlowControl(flowControl)\n

\u53c2\u6570\uff1a - flowControl (str): \u6d41\u63a7\u5236\u6a21\u5f0f\uff08'none'\u3001'hardware'\u3001'software'\uff09

"},{"location":"qsl4a/hardware/usbserial/#usbhostserialreadhex","title":"usbHostSerialReadHex()","text":"

\u4ee5\u5341\u516d\u8fdb\u5236\u5b57\u7b26\u4e32\u8bfb\u53d6\u6570\u636e\u3002

usbHostSerialReadHex(bufferSize=1024)\n

\u53c2\u6570\uff1a - bufferSize (int): \u8981\u8bfb\u53d6\u7684\u6700\u5927\u5b57\u8282\u6570

\u8fd4\u56de\uff1a \u5341\u516d\u8fdb\u5236\u5b57\u7b26\u4e32

"},{"location":"qsl4a/hardware/usbserial/#usbhostserialwritehex","title":"usbHostSerialWriteHex()","text":"

\u4ece\u5341\u516d\u8fdb\u5236\u5b57\u7b26\u4e32\u5199\u5165\u6570\u636e\u3002

usbHostSerialWriteHex(hexString)\n

\u53c2\u6570\uff1a - hexString (str): \u8981\u5199\u5165\u7684\u5341\u516d\u8fdb\u5236\u5b57\u7b26\u4e32

"},{"location":"qsl4a/hardware/usbserial/#_2","title":"\u4f7f\u7528\u793a\u4f8b","text":"
import androidhelper\n\ndroid = androidhelper.Android()\n\n# \u6253\u5f00 USB \u4e32\u884c\u8fde\u63a5\nif droid.usbHostSerialOpen(\"/dev/bus/usb/001/001\", 115200).result:\n    print(\"USB serial opened\")\n\n    # \u5199\u5165\u6570\u636e\n    droid.usbHostSerialWrite(\"AT\\r\")\n\n    # \u8bfb\u53d6\u54cd\u5e94\n    response = droid.usbHostSerialRead(1024).result\n    print(f\"Response: {response}\")\n\n    # \u6216\u4f7f\u7528\u5341\u516d\u8fdb\u5236\n    droid.usbHostSerialWriteHex(\"41540D0A\")  # \"AT\\r\\n\"\n\n    # \u5173\u95ed\u8fde\u63a5\n    droid.usbHostSerialClose()\n

\u6ce8\u610f\uff1a USB \u4e32\u884c\u9700\u8981\uff1a - Android 3.1+ (API 12) - USB OTG \u7ebf/\u9002\u914d\u5668 - \u8bbe\u5907\u4e0a\u7684 USB \u4e3b\u673a\u6a21\u5f0f\u652f\u6301 - \u517c\u5bb9\u7684\u4e32\u884c\u8bbe\u5907

"},{"location":"qsl4a/hardware/webcam/","title":"\u7f51\u7edc\u6444\u50cf\u5934 API","text":"

\u4f7f\u7528 MJPEG \u4ece\u8bbe\u5907\u76f8\u673a\u6d41\u5f0f\u4f20\u8f93\u89c6\u9891\u3002

"},{"location":"qsl4a/hardware/webcam/#mjpeg","title":"MJPEG \u6d41\u65b9\u6cd5","text":""},{"location":"qsl4a/hardware/webcam/#webcamstart","title":"webcamStart()","text":"

\u4ece\u7f51\u7edc\u6444\u50cf\u5934\u542f\u52a8 MJPEG \u6d41\u3002

webcamStart(resolutionLevel=0, jpegQuality=20, port=0)\n

\u53c2\u6570\uff1a - resolutionLevel (int): \u5206\u8fa8\u7387\u7ea7\u522b\uff08\u9ed8\u8ba4\uff1a0\uff09 - jpegQuality (int): JPEG \u8d28\u91cf 1-100\uff08\u9ed8\u8ba4\uff1a20\uff09 - port (int): \u7aef\u53e3\u53f7\uff08\u9ed8\u8ba4\uff1a0 = \u81ea\u52a8\uff09

\u8fd4\u56de\uff1a \u6d41\u7684 (\u5730\u5740, \u7aef\u53e3) \u5143\u7ec4

"},{"location":"qsl4a/hardware/webcam/#webcamadjustquality","title":"webcamAdjustQuality()","text":"

\u8c03\u6574\u6d3b\u52a8\u7f51\u7edc\u6444\u50cf\u5934\u6d41\u7684\u8d28\u91cf\u3002

webcamAdjustQuality(resolutionLevel=0, jpegQuality=20)\n

\u53c2\u6570\uff1a - resolutionLevel (int): \u5206\u8fa8\u7387\u7ea7\u522b - jpegQuality (int): JPEG \u8d28\u91cf 1-100

"},{"location":"qsl4a/hardware/webcam/#webcamstop","title":"webcamStop()","text":"

\u505c\u6b62\u7f51\u7edc\u6444\u50cf\u5934\u6d41\u3002

webcamStop()\n
"},{"location":"qsl4a/hardware/webcam/#_1","title":"\u76f8\u673a\u9884\u89c8\u65b9\u6cd5","text":""},{"location":"qsl4a/hardware/webcam/#camerastartpreview","title":"cameraStartPreview()","text":"

\u5f00\u59cb\u5e26\u4e8b\u4ef6\u751f\u6210\u7684\u76f8\u673a\u9884\u89c8\u6a21\u5f0f\u3002

cameraStartPreview(resolutionLevel=0, jpegQuality=20, filepath=None)\n

\u53c2\u6570\uff1a - resolutionLevel (int): \u5206\u8fa8\u7387\u7ea7\u522b\uff08\u9ed8\u8ba4\uff1a0\uff09 - jpegQuality (int): JPEG \u8d28\u91cf\uff08\u9ed8\u8ba4\uff1a20\uff09 - filepath (str, optional): \u4fdd\u5b58\u9884\u89c8\u5e27\u7684\u6587\u4ef6\u8def\u5f84

\u8fd4\u56de\uff1a \u5982\u679c\u6210\u529f\u5219\u4e3a True

\u6ce8\u610f\uff1a \u751f\u6210\u5e26\u6709\u5e27\u6570\u636e\u7684 'preview' \u4e8b\u4ef6\u3002

"},{"location":"qsl4a/hardware/webcam/#camerastoppreview","title":"cameraStopPreview()","text":"

\u505c\u6b62\u76f8\u673a\u9884\u89c8\u3002

cameraStopPreview()\n
"},{"location":"qsl4a/hardware/webcam/#_2","title":"\u4f7f\u7528\u793a\u4f8b","text":"
import androidhelper\nimport time\n\ndroid = androidhelper.Android()\n\n# \u542f\u52a8\u7f51\u7edc\u6444\u50cf\u5934\u6d41\nstream_info = droid.webcamStart(\n    resolutionLevel=0,\n    jpegQuality=30,\n    port=8080\n).result\nprint(f\"Stream available at {stream_info[0]}:{stream_info[1]}\")\n\n# \u6d41\u5f0f\u4f20\u8f93\u65f6\u8c03\u6574\u8d28\u91cf\ntime.sleep(5)\ndroid.webcamAdjustQuality(resolutionLevel=1, jpegQuality=50)\n\n# \u5b8c\u6210\u540e\u505c\u6b62\ndroid.webcamStop()\n\n# \u6216\u4f7f\u7528\u9884\u89c8\u6a21\u5f0f\nprint(\"Starting preview...\")\ndroid.cameraStartPreview()\n\n# \u7b49\u5f85\u9884\u89c8\u4e8b\u4ef6\nfor i in range(10):\n    event = droid.eventWait(timeout=1).result\n    if event and event['name'] == 'preview':\n        print(f\"Got preview frame: {event['data']}\")\n\ndroid.cameraStopPreview()\n
"},{"location":"qsl4a/media/image/","title":"\u56fe\u50cf\u5904\u7406 API","text":"

\u538b\u7f29\u548c\u5904\u7406\u56fe\u50cf\u3002

"},{"location":"qsl4a/media/image/#_1","title":"\u56fe\u50cf\u538b\u7f29","text":""},{"location":"qsl4a/media/image/#imagecompress","title":"imageCompress()","text":"

\u538b\u7f29\u56fe\u50cf\u6587\u4ef6\u3002

imageCompress(srcPath, destPath, targetByteSize=0, targetWidth=0, targetHeight=0)\n

\u53c2\u6570\uff1a - srcPath (str): \u6e90\u56fe\u50cf\u8def\u5f84 - destPath (str): \u8f93\u51fa\u8def\u5f84 - targetByteSize (int): \u76ee\u6807\u6587\u4ef6\u5927\u5c0f\uff08\u5b57\u8282\uff09\uff080 = \u65e0\u9650\u5236\uff09 - targetWidth (int): \u76ee\u6807\u5bbd\u5ea6\uff080 = \u539f\u59cb\uff09 - targetHeight (int): \u76ee\u6807\u9ad8\u5ea6\uff080 = \u539f\u59cb\uff09

\u8fd4\u56de\uff1a \u538b\u7f29\u540e\u7684\u56fe\u50cf\u8def\u5f84

"},{"location":"qsl4a/media/image/#_2","title":"\u622a\u56fe","text":""},{"location":"qsl4a/media/image/#imagereadergetscreenshot","title":"imageReaderGetScreenShot()","text":"

\u622a\u53d6\u5c4f\u5e55\u3002

imageReaderGetScreenShot(path=None, delayMilliSec=1000)\n

\u53c2\u6570\uff1a - path (str): \u4fdd\u5b58\u8def\u5f84 - delayMilliSec (int): \u62cd\u6444\u524d\u5ef6\u8fdf

\u8fd4\u56de\uff1a \u622a\u56fe\u8def\u5f84

"},{"location":"qsl4a/media/image/#_3","title":"\u89c6\u9891\u64ad\u653e","text":""},{"location":"qsl4a/media/image/#videoplay","title":"videoPlay()","text":"

\u5728\u5168\u5c4f\u6a21\u5f0f\u4e0b\u64ad\u653e\u89c6\u9891\u6587\u4ef6\u3002

videoPlay(path, wait=True)\n

\u53c2\u6570\uff1a - path (str): \u89c6\u9891\u6587\u4ef6\u8def\u5f84 - wait (bool): \u7b49\u5f85\u64ad\u653e\u5b8c\u6210\uff08\u9ed8\u8ba4\uff1aTrue\uff09

"},{"location":"qsl4a/media/image/#_4","title":"\u6761\u7801\u626b\u63cf","text":""},{"location":"qsl4a/media/image/#scanbarcodefromimage","title":"scanBarcodeFromImage()","text":"

\u4ece\u56fe\u50cf\u6587\u4ef6\u626b\u63cf\u6761\u7801/\u4e8c\u7ef4\u7801\u3002

scanBarcodeFromImage(path, compressRatio=0, x=0, y=0, width=0, height=0)\n

\u53c2\u6570\uff1a - path (str): \u56fe\u50cf\u6587\u4ef6\u8def\u5f84 - compressRatio (int): \u538b\u7f29\u6bd4\uff080 = \u4e0d\u538b\u7f29\uff09 - x, y, width, height (int): \u8981\u626b\u63cf\u7684\u533a\u57df\uff080 = \u5b8c\u6574\u56fe\u50cf\uff09

\u8fd4\u56de\uff1a \u626b\u63cf\u7684\u6761\u7801\u5185\u5bb9

"},{"location":"qsl4a/media/image/#_5","title":"\u4f7f\u7528\u793a\u4f8b","text":"
import androidhelper\n\ndroid = androidhelper.Android()\n\n# \u622a\u56fe\nss_path = droid.imageReaderGetScreenShot(\"/sdcard/screenshot.png\", 500).result\n\n# \u538b\u7f29\u56fe\u50cf\ncompressed = droid.imageCompress(\n    \"/sdcard/large_photo.jpg\",\n    \"/sdcard/compressed.jpg\",\n    targetByteSize=102400,  # \u76ee\u6807\u7ea6 100KB\n    targetWidth=1920,\n    targetHeight=1080\n).result\nprint(f\"Saved: {compressed}\")\n\n# \u64ad\u653e\u89c6\u9891\nvideo_path = \"/sdcard/movie.mp4\"\ndroid.videoPlay(video_path, wait=True)\n\n# \u4ece\u56fe\u50cf\u626b\u63cf\u6761\u7801\nresult = droid.scanBarcodeFromImage(\"/sdcard/qr_code.png\").result\nprint(f\"Barcode: {result}\")\n
"},{"location":"qsl4a/media/mediaplayer/","title":"\u5a92\u4f53\u64ad\u653e\u5668 API","text":"

\u63a7\u5236\u97f3\u9891\u548c\u89c6\u9891\u64ad\u653e\u3002

"},{"location":"qsl4a/media/mediaplayer/#_1","title":"\u64ad\u653e\u63a7\u5236","text":""},{"location":"qsl4a/media/mediaplayer/#mediaplay","title":"mediaPlay()","text":"

\u64ad\u653e\u5a92\u4f53\u6587\u4ef6\u3002

mediaPlay(url, tag=\"default\", play=True)\n

\u53c2\u6570\uff1a - url (str): \u5a92\u4f53\u6587\u4ef6\u8def\u5f84\u6216 URL - tag (str): \u64ad\u653e\u5668\u6807\u8bc6\u7b26 - play (bool): \u81ea\u52a8\u5f00\u59cb\u64ad\u653e

"},{"location":"qsl4a/media/mediaplayer/#mediaplaystart","title":"mediaPlayStart()","text":"

\u5f00\u59cb\u64ad\u653e\u3002

mediaPlayStart(tag=\"default\")\n
"},{"location":"qsl4a/media/mediaplayer/#mediaplaypause","title":"mediaPlayPause()","text":"

\u6682\u505c\u64ad\u653e\u3002

mediaPlayPause(tag=\"default\")\n
"},{"location":"qsl4a/media/mediaplayer/#mediaplayclose","title":"mediaPlayClose()","text":"

\u5173\u95ed\u64ad\u653e\u5668\u3002

mediaPlayClose(tag=\"default\")\n
"},{"location":"qsl4a/media/mediaplayer/#mediaplayseek","title":"mediaPlaySeek()","text":"

\u8df3\u8f6c\u5230\u4f4d\u7f6e\u3002

mediaPlaySeek(msec, tag=\"default\")\n

\u53c2\u6570\uff1a - msec (int): \u4f4d\u7f6e\uff08\u6beb\u79d2\uff09

"},{"location":"qsl4a/media/mediaplayer/#mediaplaysetlooping","title":"mediaPlaySetLooping()","text":"

\u8bbe\u7f6e\u5faa\u73af\u6a21\u5f0f\u3002

mediaPlaySetLooping(enabled, tag=\"default\")\n
"},{"location":"qsl4a/media/mediaplayer/#_2","title":"\u64ad\u653e\u5668\u4fe1\u606f","text":""},{"location":"qsl4a/media/mediaplayer/#mediaplayinfo","title":"mediaPlayInfo()","text":"

\u83b7\u53d6\u64ad\u653e\u4fe1\u606f\u3002

mediaPlayInfo(tag=\"default\")\n

\u8fd4\u56de\uff1a \u5305\u542b\u65f6\u957f\u3001\u4f4d\u7f6e\u7b49\u7684\u5b57\u5178

"},{"location":"qsl4a/media/mediaplayer/#mediaisplaying","title":"mediaIsPlaying()","text":"

\u68c0\u67e5\u662f\u5426\u6b63\u5728\u64ad\u653e\u3002

mediaIsPlaying(tag=\"default\")\n

\u8fd4\u56de\uff1a True/False

"},{"location":"qsl4a/media/mediaplayer/#mediaplaylist","title":"mediaPlayList()","text":"

\u5217\u51fa\u6d3b\u52a8\u64ad\u653e\u5668\u3002

mediaPlayList()\n
"},{"location":"qsl4a/media/mediaplayer/#_3","title":"\u97f3\u91cf\u63a7\u5236","text":""},{"location":"qsl4a/media/mediaplayer/#getmediavolume","title":"getMediaVolume()","text":"

\u83b7\u53d6\u5a92\u4f53\u97f3\u91cf\u3002

getMediaVolume()\n

\u8fd4\u56de\uff1a \u97f3\u91cf\u7ea7\u522b\uff080-15\uff09

"},{"location":"qsl4a/media/mediaplayer/#getmaxmediavolume","title":"getMaxMediaVolume()","text":"

\u83b7\u53d6\u6700\u5927\u5a92\u4f53\u97f3\u91cf\u3002

getMaxMediaVolume()\n
"},{"location":"qsl4a/media/mediaplayer/#getringervolume","title":"getRingerVolume()","text":"

\u83b7\u53d6\u94c3\u58f0\u97f3\u91cf\u3002

getRingerVolume()\n
"},{"location":"qsl4a/media/mediaplayer/#getmaxringervolume","title":"getMaxRingerVolume()","text":"

\u83b7\u53d6\u6700\u5927\u94c3\u58f0\u97f3\u91cf\u3002

getMaxRingerVolume()\n
"},{"location":"qsl4a/media/mediaplayer/#_4","title":"\u89c6\u9891\u64ad\u653e","text":""},{"location":"qsl4a/media/mediaplayer/#videoplay","title":"videoPlay()","text":"

\u5168\u5c4f\u64ad\u653e\u89c6\u9891\u3002

videoPlay(path, wait=True)\n

\u53c2\u6570\uff1a - path (str): \u89c6\u9891\u6587\u4ef6\u8def\u5f84 - wait (bool): \u7b49\u5f85\u64ad\u653e\u5b8c\u6210

"},{"location":"qsl4a/media/mediaplayer/#_5","title":"\u4f7f\u7528\u793a\u4f8b","text":"
import androidhelper\n\ndroid = androidhelper.Android()\n\n# \u64ad\u653e\u97f3\u9891\ndroid.mediaPlay(\"/sdcard/music.mp3\", tag=\"music\")\n\n# \u68c0\u67e5\u72b6\u6001\nif droid.mediaIsPlaying(\"music\").result:\n    info = droid.mediaPlayInfo(\"music\").result\n    print(f\"Playing: {info}\")\n\n# \u8df3\u8f6c\u5230 30 \u79d2\ndroid.mediaPlaySeek(30000, \"music\")\n\n# \u5173\u95ed\ndroid.mediaPlayClose(\"music\")\n\n# \u64ad\u653e\u89c6\u9891\ndroid.videoPlay(\"/sdcard/movie.mp4\", wait=True)\n
"},{"location":"qsl4a/special/cipher/","title":"\u5bc6\u7801 API","text":"

\u7528\u4e8e\u5b89\u5168\u6570\u636e\u5b58\u50a8\u7684\u52a0\u5bc6\u548c\u89e3\u5bc6\u5de5\u5177\u3002

"},{"location":"qsl4a/special/cipher/#_1","title":"\u521d\u59cb\u5316","text":""},{"location":"qsl4a/special/cipher/#cipherinit","title":"cipherInit()","text":"

\u4f7f\u7528\u52a0\u5bc6\u5bc6\u94a5\u548c\u7b97\u6cd5\u521d\u59cb\u5316\u5bc6\u7801\u5668\u3002

cipherInit(key, algorithm=\"AES/CBC/PKCS5Padding\", encodingFormat=\"\", initialVector=\"\")\n

\u53c2\u6570\uff1a - key (str or bytes): \u52a0\u5bc6\u5bc6\u94a5 - algorithm (str): \u5bc6\u7801\u7b97\u6cd5\uff08\u9ed8\u8ba4\uff1a\"AES/CBC/PKCS5Padding\"\uff09 - encodingFormat (str): \u7f16\u7801\u683c\u5f0f - initialVector (str or bytes): \u52a0\u5bc6\u7684\u521d\u59cb\u5411\u91cf

\u8fd4\u56de\uff1a \u521d\u59cb\u5316\u7ed3\u679c

\u6ce8\u610f\uff1a \u5fc5\u987b\u5728\u4efb\u4f55\u52a0\u5bc6/\u89e3\u5bc6\u64cd\u4f5c\u4e4b\u524d\u8c03\u7528\u3002

"},{"location":"qsl4a/special/cipher/#_2","title":"\u52a0\u5bc6\u65b9\u6cd5","text":""},{"location":"qsl4a/special/cipher/#encryptstring","title":"encryptString()","text":"

\u52a0\u5bc6\u5b57\u7b26\u4e32\u3002

encryptString(plainText)\n

\u53c2\u6570\uff1a - plainText (str): \u8981\u52a0\u5bc6\u7684\u6587\u672c

\u8fd4\u56de\uff1a \u52a0\u5bc6\u7684\u5b57\u7b26\u4e32

"},{"location":"qsl4a/special/cipher/#encryptbytes","title":"encryptBytes()","text":"

\u52a0\u5bc6\u5b57\u8282\u6570\u636e\u3002

encryptBytes(data)\n

\u53c2\u6570\uff1a - data (bytes): \u8981\u52a0\u5bc6\u7684\u6570\u636e

\u8fd4\u56de\uff1a \u52a0\u5bc6\u7684\u5b57\u8282

"},{"location":"qsl4a/special/cipher/#encryptstringtofile","title":"encryptStringToFile()","text":"

\u52a0\u5bc6\u5b57\u7b26\u4e32\u5230\u6587\u4ef6\u3002

encryptStringToFile(plainText, filePath)\n

\u53c2\u6570\uff1a - plainText (str): \u8981\u52a0\u5bc6\u7684\u6587\u672c - filePath (str): \u8f93\u51fa\u6587\u4ef6\u8def\u5f84

"},{"location":"qsl4a/special/cipher/#encryptbytestofile","title":"encryptBytesToFile()","text":"

\u52a0\u5bc6\u5b57\u8282\u5230\u6587\u4ef6\u3002

encryptBytesToFile(data, filePath)\n
"},{"location":"qsl4a/special/cipher/#_3","title":"\u89e3\u5bc6\u65b9\u6cd5","text":""},{"location":"qsl4a/special/cipher/#decryptstring","title":"decryptString()","text":"

\u89e3\u5bc6\u4e3a\u5b57\u7b26\u4e32\u3002

decryptString(cipherText)\n

\u53c2\u6570\uff1a - cipherText (str): \u52a0\u5bc6\u7684\u6587\u672c

\u8fd4\u56de\uff1a \u89e3\u5bc6\u7684\u5b57\u7b26\u4e32

"},{"location":"qsl4a/special/cipher/#decryptbytes","title":"decryptBytes()","text":"

\u89e3\u5bc6\u4e3a\u5b57\u8282\u3002

decryptBytes(data)\n

\u8fd4\u56de\uff1a \u89e3\u5bc6\u7684\u5b57\u8282

"},{"location":"qsl4a/special/cipher/#decryptfiletostring","title":"decryptFileToString()","text":"

\u89e3\u5bc6\u6587\u4ef6\u5230\u5b57\u7b26\u4e32\u3002

decryptFileToString(filePath)\n
"},{"location":"qsl4a/special/cipher/#decryptfiletobytes","title":"decryptFileToBytes()","text":"

\u89e3\u5bc6\u6587\u4ef6\u5230\u5b57\u8282\u3002

decryptFileToBytes(filePath)\n
"},{"location":"qsl4a/special/cipher/#decryptfile","title":"decryptFile()","text":"

\u89e3\u5bc6\u6587\u4ef6\u5230\u6587\u4ef6\u3002

decryptFile(srcPath, destPath)\n
"},{"location":"qsl4a/special/cipher/#_4","title":"\u4f7f\u7528\u793a\u4f8b","text":"
import androidhelper\n\ndroid = androidhelper.Android()\n\n# \u4f7f\u7528\u52a0\u5bc6\u5bc6\u94a5\u521d\u59cb\u5316\u5bc6\u7801\u5668\ndroid.cipherInit(\"my_secret_key_1234\")\n\n# \u52a0\u5bc6\u5b57\u7b26\u4e32\nencrypted = droid.encryptString(\"Secret message!\").result\nprint(f\"Encrypted: {encrypted}\")\n\n# \u89e3\u5bc6\u5b57\u7b26\u4e32\ndecrypted = droid.decryptString(encrypted).result\nprint(f\"Decrypted: {decrypted}\")\n\n# \u52a0\u5bc6\u5230\u6587\u4ef6\ndroid.encryptStringToFile(\"My secret data\", \"/sdcard/secret.dat\")\n\n# \u4ece\u6587\u4ef6\u89e3\u5bc6\ndata = droid.decryptFileToString(\"/sdcard/secret.dat\").result\nprint(data)\n\n# \u4f7f\u7528\u81ea\u5b9a\u4e49\u7b97\u6cd5\u548c IV \u521d\u59cb\u5316\ndroid.cipherInit(\n    key=\"my_key\",\n    algorithm=\"AES/CBC/PKCS5Padding\",\n    initialVector=\"0123456789abcdef\"\n)\n
"},{"location":"qsl4a/special/pgptai/","title":"PGPT AI API","text":"

\u8bed\u97f3\u8f6c\u6587\u672c\u548c AI \u670d\u52a1\u96c6\u6210\u3002

"},{"location":"qsl4a/special/pgptai/#_1","title":"\u5148\u51b3\u6761\u4ef6","text":"
pip install pgptAI\n
"},{"location":"qsl4a/special/pgptai/#_2","title":"\u8bed\u97f3\u8bc6\u522b","text":""},{"location":"qsl4a/special/pgptai/#speechtotext","title":"speechToText()","text":"

\u5c06\u8bed\u97f3\u8f6c\u6362\u4e3a\u6587\u672c\u3002

speechToText(RecordSecond=10, AmrFile=None, Language=None)\n

\u53c2\u6570\uff1a - RecordSecond (int): \u5f55\u5236\u65f6\u957f\uff08\u79d2\uff09 - AmrFile (str, optional): \u73b0\u6709\u97f3\u9891\u6587\u4ef6\u8def\u5f84 - Language (str, optional): \u8bed\u8a00\u4ee3\u7801\uff08'en'\u3001'zh'\uff09

\u8fd4\u56de\uff1a \u8f6c\u5f55\u7684\u6587\u672c

"},{"location":"qsl4a/special/pgptai/#_3","title":"\u6587\u672c\u8f6c\u8bed\u97f3","text":""},{"location":"qsl4a/special/pgptai/#texttospeech","title":"textToSpeech()","text":"

\u5c06\u6587\u672c\u8f6c\u6362\u4e3a\u8bed\u97f3\u5e76\u53ef\u9009\u5730\u64ad\u653e\u3002

textToSpeech(Text, AutoPlay=True, WavFile=None, VoiceName=None)\n

\u53c2\u6570\uff1a - Text (str): \u8981\u8f6c\u6362\u4e3a\u8bed\u97f3\u7684\u6587\u672c - AutoPlay (bool): \u81ea\u52a8\u64ad\u653e\u751f\u6210\u7684\u97f3\u9891\uff08\u9ed8\u8ba4\uff1aTrue\uff09 - WavFile (str, optional): \u4fdd\u5b58 WAV \u6587\u4ef6\u7684\u8def\u5f84\u3002\u5982\u679c\u4e3a None\uff0c\u5219\u4f7f\u7528\u4e34\u65f6\u6587\u4ef6 - VoiceName (str, optional): \u8981\u4f7f\u7528\u7684\u8bed\u97f3\u540d\u79f0\uff08\u4f8b\u5982 'en-US-JennyNeural'\u3001'zh-CN-XiaoxiaoNeural'\uff09

\u8fd4\u56de\uff1a \u5305\u542b\u8bed\u97f3\u5408\u6210\u7ed3\u679c\u7684\u5b57\u5178\uff0c\u5305\u62ec\uff1a - text: \u8f93\u5165\u6587\u672c - url: \u4e0b\u8f7d\u97f3\u9891\u6587\u4ef6\u7684 URL - WavFile: \u4fdd\u5b58\u7684 WAV \u6587\u4ef6\u8def\u5f84\uff08\u5982\u679c\u672c\u5730\u4fdd\u5b58\uff09

"},{"location":"qsl4a/special/pgptai/#_4","title":"\u914d\u7f6e","text":"

API \u4f7f\u7528\u4ee5\u4e0b\u914d\u7f6e\u6587\u4ef6\u4e2d\u7684\u914d\u7f6e\uff1a/storage/emulated/0/Android/data/org.qpython.qpy/files/apigpt.conf\uff1a

[speech]\nspeech_key = your_api_key\n

\u9ed8\u8ba4\u8bed\u97f3\u8bbe\u7f6e\uff1a - \u82f1\u8bed\uff1aen-US-JennyNeural - \u4e2d\u6587\uff1azh-CN-XiaoxiaoNeural

"},{"location":"qsl4a/special/pgptai/#_5","title":"\u4f7f\u7528\u793a\u4f8b","text":"
import androidhelper\n\ndroid = androidhelper.Android()\n\n# \u5f55\u5236\u5e76\u8f6c\u5f55\nprint(\"Recording for 5 seconds...\")\ntext = droid.speechToText(RecordSecond=5, Language='en').result\nprint(f\"You said: {text}\")\n\n# \u8f6c\u5f55\u73b0\u6709\u6587\u4ef6\ntext = droid.speechToText(AmrFile=\"/sdcard/recording.amr\").result\nprint(f\"Transcription: {text}\")\n\n# \u6587\u672c\u8f6c\u8bed\u97f3\ndroid.textToSpeech(\"Hello, this is a test message.\", AutoPlay=True, Language='en')\n\n# \u4f7f\u7528\u81ea\u5b9a\u4e49\u8bed\u97f3\u5e76\u4fdd\u5b58\u5230\u6587\u4ef6\u7684\u6587\u672c\u8f6c\u8bed\u97f3\nresult = droid.textToSpeech(\n    \"Welcome to QPython!\",\n    AutoPlay=False,\n    WavFile=\"/sdcard/welcome.wav\",\n    VoiceName=\"en-US-JennyNeural\"\n).result\nprint(f\"Audio saved to: {result.get('WavFile')}\")\n
"},{"location":"qsl4a/special/pgptai/#_6","title":"\u7c7b\u4f7f\u7528","text":"
from androidhelper.pgptai import pgptai\nimport androidhelper\n\ndroid = androidhelper.Android()\nai = pgptai(droid)\n\n# \u4f7f\u7528\u8bed\u97f3\u8bc6\u522b\ntext = ai.speechToText(RecordSecond=10)\n
"},{"location":"qsl4a/storage/clipboard/","title":"\u526a\u8d34\u677f API","text":"

\u590d\u5236\u548c\u7c98\u8d34\u6587\u672c\u5230\u7cfb\u7edf\u526a\u8d34\u677f\u3002

"},{"location":"qsl4a/storage/clipboard/#_1","title":"\u65b9\u6cd5","text":""},{"location":"qsl4a/storage/clipboard/#setclipboard","title":"setClipboard()","text":"

\u590d\u5236\u6587\u672c\u5230\u526a\u8d34\u677f\u3002

setClipboard(text)\n

\u53c2\u6570\uff1a - text (str): \u8981\u590d\u5236\u7684\u6587\u672c

\u8fd4\u56de\uff1a \u5982\u679c\u6210\u529f\u5219\u4e3a True

"},{"location":"qsl4a/storage/clipboard/#getclipboard","title":"getClipboard()","text":"

\u4ece\u526a\u8d34\u677f\u83b7\u53d6\u6587\u672c\u3002

getClipboard()\n

\u8fd4\u56de\uff1a \u526a\u8d34\u677f\u6587\u672c

"},{"location":"qsl4a/storage/clipboard/#_2","title":"\u4f7f\u7528\u793a\u4f8b","text":"
import androidhelper\n\ndroid = androidhelper.Android()\n\n# \u590d\u5236\u5230\u526a\u8d34\u677f\ndroid.setClipboard(\"Hello from QPython!\")\n\n# \u4ece\u526a\u8d34\u677f\u7c98\u8d34\ntext = droid.getClipboard().result\nprint(f\"Clipboard: {text}\")\n
"},{"location":"qsl4a/storage/documentfile/","title":"DocumentFile API","text":"

\u4f7f\u7528 SAF\uff08\u5b58\u50a8\u8bbf\u95ee\u6846\u67b6\uff09\u8fdb\u884c\u6587\u4ef6\u64cd\u4f5c\uff0c\u652f\u6301 Android 4.4+\u3002

"},{"location":"qsl4a/storage/documentfile/#_1","title":"\u76ee\u5f55\u64cd\u4f5c","text":""},{"location":"qsl4a/storage/documentfile/#documentfilemkdir","title":"documentFileMkdir()","text":"

\u521b\u5efa\u76ee\u5f55\u3002

documentFileMkdir(Dir)\n

\u53c2\u6570\uff1a - Dir (str): \u76ee\u5f55\u8def\u5f84

\u8fd4\u56de\uff1a \u5982\u679c\u6210\u529f\u5219\u4e3a True

"},{"location":"qsl4a/storage/documentfile/#documentfilelistfiles","title":"documentFileListFiles()","text":"

\u5217\u51fa\u76ee\u5f55\u4e2d\u7684\u6587\u4ef6\u3002

documentFileListFiles(Folder)\n

\u8fd4\u56de\uff1a \u6587\u4ef6\u5217\u8868

"},{"location":"qsl4a/storage/documentfile/#_2","title":"\u6587\u4ef6\u64cd\u4f5c","text":""},{"location":"qsl4a/storage/documentfile/#documentfileexists","title":"documentFileExists()","text":"

\u68c0\u67e5\u6587\u4ef6\u6216\u76ee\u5f55\u662f\u5426\u5b58\u5728\u3002

documentFileExists(path)\n

\u53c2\u6570\uff1a - path (str): \u6587\u4ef6\u6216\u76ee\u5f55\u8def\u5f84

\u8fd4\u56de\uff1a \u5982\u679c\u5b58\u5728\u5219\u4e3a True\uff0c\u5426\u5219\u4e3a False

"},{"location":"qsl4a/storage/documentfile/#documentfileisfile","title":"documentFileIsFile()","text":"

\u68c0\u67e5\u8def\u5f84\u662f\u5426\u662f\u6587\u4ef6\u3002

documentFileIsFile(path)\n

\u53c2\u6570\uff1a - path (str): \u8981\u68c0\u67e5\u7684\u8def\u5f84

\u8fd4\u56de\uff1a \u5982\u679c\u662f\u6587\u4ef6\u5219\u4e3a True\uff0c\u5982\u679c\u4e0d\u662f\u6587\u4ef6\u5219\u4e3a False\uff0c\u5982\u679c\u4e0d\u5b58\u5728\u5219\u4e3a None

"},{"location":"qsl4a/storage/documentfile/#documentfileisdirectory","title":"documentFileIsDirectory()","text":"

\u68c0\u67e5\u8def\u5f84\u662f\u5426\u662f\u76ee\u5f55\u3002

documentFileIsDirectory(path)\n

\u53c2\u6570\uff1a - path (str): \u8981\u68c0\u67e5\u7684\u8def\u5f84

\u8fd4\u56de\uff1a \u5982\u679c\u662f\u76ee\u5f55\u5219\u4e3a True\uff0c\u5982\u679c\u4e0d\u662f\u76ee\u5f55\u5219\u4e3a False\uff0c\u5982\u679c\u4e0d\u5b58\u5728\u5219\u4e3a None

"},{"location":"qsl4a/storage/documentfile/#documentfiledelete","title":"documentFileDelete()","text":"

\u5220\u9664\u6587\u4ef6\u6216\u76ee\u5f55\u3002

documentFileDelete(FileOrTree)\n

\u8fd4\u56de\uff1a \u5982\u679c\u6210\u529f\u5219\u4e3a True

"},{"location":"qsl4a/storage/documentfile/#documentfilerenameto","title":"documentFileRenameTo()","text":"

\u91cd\u547d\u540d\u6216\u79fb\u52a8\u6587\u4ef6\u3002

documentFileRenameTo(Src, Dest)\n

\u8fd4\u56de\uff1a \u5982\u679c\u6210\u529f\u5219\u4e3a True

"},{"location":"qsl4a/storage/documentfile/#documentfilecopy","title":"documentFileCopy()","text":"

\u590d\u5236\u6587\u4ef6\u3002

documentFileCopy(SrcFileOrTree, DestFileOrTree)\n
"},{"location":"qsl4a/storage/documentfile/#_3","title":"\u6d41\u64cd\u4f5c","text":""},{"location":"qsl4a/storage/documentfile/#documentfileinputstream","title":"documentFileInputStream()","text":"

\u8bfb\u53d6\u6587\u4ef6\u5185\u5bb9\u3002

documentFileInputStream(srcFile, EncodingFormat=\"\", skip=None, length=None)\n

\u53c2\u6570\uff1a - srcFile (str): \u6e90\u6587\u4ef6 - EncodingFormat (str): \"UTF-8\"\u3001\"GBK\"\u3001\"Base64\" \u6216\u7a7a\u5b57\u7b26\u4e32\u8868\u793a\u5b57\u8282 - skip (int): \u4ece\u5f00\u5934\u8df3\u8fc7\u7684\u5b57\u8282\u6570 - length (int): \u8bfb\u53d6\u957f\u5ea6

\u8fd4\u56de\uff1a \u6587\u4ef6\u5185\u5bb9

"},{"location":"qsl4a/storage/documentfile/#documentfileoutputstream","title":"documentFileOutputStream()","text":"

\u5199\u5165\u6587\u4ef6\u5185\u5bb9\u3002

documentFileOutputStream(destFile, src, EncodingFormat=\"\", append=None)\n

\u53c2\u6570\uff1a - destFile (str): \u76ee\u6807\u6587\u4ef6 - src: \u8981\u5199\u5165\u7684\u6570\u636e - EncodingFormat (str): \u7f16\u7801\u683c\u5f0f - append (bool): \u8ffd\u52a0\u6a21\u5f0f

"},{"location":"qsl4a/storage/documentfile/#_4","title":"\u6587\u4ef6\u4fe1\u606f","text":""},{"location":"qsl4a/storage/documentfile/#documentfilelength","title":"documentFileLength()","text":"

\u83b7\u53d6\u6587\u4ef6\u5927\u5c0f\uff08\u5b57\u8282\uff09\u3002

documentFileLength(path)\n

\u53c2\u6570\uff1a - path (str): \u6587\u4ef6\u8def\u5f84

\u8fd4\u56de\uff1a \u6587\u4ef6\u5927\u5c0f\uff08\u5b57\u8282\uff09\uff08\u5982\u679c\u4e0d\u5b58\u5728\u5219\u4e3a 0\uff09

"},{"location":"qsl4a/storage/documentfile/#documentfilelastmodified","title":"documentFileLastModified()","text":"

\u83b7\u53d6\u6700\u540e\u4fee\u6539\u65f6\u95f4\u3002

documentFileLastModified(path)\n

\u53c2\u6570\uff1a - path (str): \u6587\u4ef6\u8def\u5f84

\u8fd4\u56de\uff1a \u65f6\u95f4\u6233\uff08\u5982\u679c\u4e0d\u5b58\u5728\u5219\u4e3a 0\uff09

"},{"location":"qsl4a/storage/documentfile/#documentfilegetstat","title":"documentFileGetStat()","text":"

\u83b7\u53d6\u5168\u9762\u7684\u6587\u4ef6\u7edf\u8ba1\u4fe1\u606f\u3002

documentFileGetStat(path)\n

\u53c2\u6570\uff1a - path (str): \u6587\u4ef6\u8def\u5f84

\u8fd4\u56de\uff1a \u5305\u542b\u957f\u5ea6\u3001\u6700\u540e\u4fee\u6539\u65f6\u95f4\u548c\u8bfb/\u5199\u6743\u9650\u7684\u5b57\u5178\uff0c\u5982\u679c\u4e0d\u5b58\u5728\u5219\u4e3a None

"},{"location":"qsl4a/storage/documentfile/#uri","title":"URI \u64cd\u4f5c","text":""},{"location":"qsl4a/storage/documentfile/#documentfilegeturi","title":"documentFileGetUri()","text":"

\u4ece\u8def\u5f84\u83b7\u53d6 URI\u3002

documentFileGetUri(path, isDirectory=None)\n
"},{"location":"qsl4a/storage/documentfile/#documentfileshowopen","title":"documentFileShowOpen()","text":"

\u663e\u793a\u6587\u4ef6\u9009\u62e9\u5668\u3002

documentFileShowOpen()\n

\u8fd4\u56de\uff1a \u9009\u62e9\u7684\u6587\u4ef6 URI

"},{"location":"qsl4a/storage/documentfile/#_5","title":"\u4f7f\u7528\u793a\u4f8b","text":"
import androidhelper\n\ndroid = androidhelper.Android()\n\n# \u521b\u5efa\u76ee\u5f55\ndroid.documentFileMkdir(\"/sdcard/MyFolder\")\n\n# \u5217\u51fa\u6587\u4ef6\nfiles = droid.documentFileListFiles(\"/sdcard\").result\nfor f in files:\n    print(f)\n\n# \u8bfb\u53d6\u6587\u4ef6\ncontent = droid.documentFileInputStream(\n    \"/sdcard/test.txt\",\n    EncodingFormat=\"UTF-8\"\n).result\nprint(content)\n\n# \u5199\u5165\u6587\u4ef6\ndroid.documentFileOutputStream(\n    \"/sdcard/output.txt\",\n    \"Hello World\",\n    EncodingFormat=\"UTF-8\"\n)\n
"},{"location":"qsl4a/storage/preferences/","title":"\u504f\u597d\u8bbe\u7f6e API","text":"

\u4f7f\u7528 Android SharedPreferences \u5b58\u50a8\u548c\u68c0\u7d22\u6570\u636e\u3002

"},{"location":"qsl4a/storage/preferences/#_1","title":"\u504f\u597d\u8bbe\u7f6e\u65b9\u6cd5","text":""},{"location":"qsl4a/storage/preferences/#prefgetvalue","title":"prefGetValue()","text":"

\u4ece\u5171\u4eab\u504f\u597d\u8bbe\u7f6e\u8bfb\u53d6\u503c\u3002

prefGetValue(key, filename=None)\n

\u53c2\u6570\uff1a - key (str): \u504f\u597d\u8bbe\u7f6e\u952e - filename (str, optional): \u504f\u597d\u8bbe\u7f6e\u6587\u4ef6\u540d

\u8fd4\u56de\uff1a \u5b58\u50a8\u7684\u503c\uff08\u4efb\u610f\u7c7b\u578b\uff09

"},{"location":"qsl4a/storage/preferences/#prefputvalue","title":"prefPutValue()","text":"

\u5199\u5165\u503c\u5230\u5171\u4eab\u504f\u597d\u8bbe\u7f6e\u3002

prefPutValue(key, value, filename=None)\n

\u53c2\u6570\uff1a - key (str): \u504f\u597d\u8bbe\u7f6e\u952e - value (object): \u8981\u5b58\u50a8\u7684\u503c - filename (str, optional): \u504f\u597d\u8bbe\u7f6e\u6587\u4ef6\u540d

"},{"location":"qsl4a/storage/preferences/#prefgetall","title":"prefGetAll()","text":"

\u83b7\u53d6\u6240\u6709\u504f\u597d\u8bbe\u7f6e\u503c\u3002

prefGetAll(filename=None)\n

\u53c2\u6570\uff1a - filename (str, optional): \u504f\u597d\u8bbe\u7f6e\u6587\u4ef6\u540d

\u8fd4\u56de\uff1a \u6240\u6709\u504f\u597d\u7684\u6620\u5c04

"},{"location":"qsl4a/storage/preferences/#prefremovevalue","title":"prefRemoveValue()","text":"

\u4ece\u5171\u4eab\u504f\u597d\u8bbe\u7f6e\u4e2d\u79fb\u9664\u503c\u3002

prefRemoveValue(key, filename=None)\n

\u53c2\u6570\uff1a - key (str): \u8981\u79fb\u9664\u7684\u504f\u597d\u8bbe\u7f6e\u952e - filename (str, optional): \u504f\u597d\u8bbe\u7f6e\u6587\u4ef6\u540d

"},{"location":"qsl4a/storage/preferences/#_2","title":"\u4f7f\u7528\u793a\u4f8b","text":"
import androidhelper\n\ndroid = androidhelper.Android()\n\n# \u5b58\u50a8\u503c\ndroid.prefPutValue(\"username\", \"alice\")\ndroid.prefPutValue(\"score\", 100)\ndroid.prefPutValue(\"enabled\", True)\n\n# \u8bfb\u53d6\u7279\u5b9a\u503c\nusername = droid.prefGetValue(\"username\").result\nprint(f\"Username: {username}\")\n\n# \u83b7\u53d6\u6240\u6709\u504f\u597d\u8bbe\u7f6e\nall_prefs = droid.prefGetAll().result\nprint(f\"All prefs: {all_prefs}\")\n\n# \u79fb\u9664\u503c\ndroid.prefRemoveValue(\"score\")\n\n# \u4f7f\u7528\u81ea\u5b9a\u4e49\u6587\u4ef6\u540d\ndroid.prefPutValue(\"token\", \"abc123\", filename=\"auth.prefs\")\ntoken = droid.prefGetValue(\"token\", filename=\"auth.prefs\").result\n
"},{"location":"qsl4a/system/activityresult/","title":"Activity Result API","text":"

\u4e3a\u901a\u8fc7 startActivityForResult \u542f\u52a8\u7684\u811a\u672c\u8bbe\u7f6e activity \u7ed3\u679c\u3002

"},{"location":"qsl4a/system/activityresult/#_1","title":"\u7ed3\u679c\u65b9\u6cd5","text":""},{"location":"qsl4a/system/activityresult/#setresultboolean","title":"setResultBoolean()","text":"

\u8bbe\u7f6e\u5e03\u5c14\u7ed3\u679c\u3002

setResultBoolean(resultCode, resultValue)\n

\u53c2\u6570\uff1a - resultCode (int): \u7ed3\u679c\u4ee3\u7801 - resultValue (bool): \u5e03\u5c14\u7ed3\u679c\u503c

"},{"location":"qsl4a/system/activityresult/#setresultbyte","title":"setResultByte()","text":"

\u8bbe\u7f6e\u5b57\u8282\u7ed3\u679c\u3002

setResultByte(resultCode, resultValue)\n

\u53c2\u6570\uff1a - resultCode (int): \u7ed3\u679c\u4ee3\u7801 - resultValue (int): \u5b57\u8282\u7ed3\u679c\u503c

"},{"location":"qsl4a/system/activityresult/#setresultshort","title":"setResultShort()","text":"

\u8bbe\u7f6e\u77ed\u6574\u6570\u7ed3\u679c\u3002

setResultShort(resultCode, resultValue)\n

\u53c2\u6570\uff1a - resultCode (int): \u7ed3\u679c\u4ee3\u7801 - resultValue (int): \u77ed\u6574\u6570\u7ed3\u679c\u503c

"},{"location":"qsl4a/system/activityresult/#setresultchar","title":"setResultChar()","text":"

\u8bbe\u7f6e\u5b57\u7b26\u7ed3\u679c\u3002

setResultChar(resultCode, resultValue)\n

\u53c2\u6570\uff1a - resultCode (int): \u7ed3\u679c\u4ee3\u7801 - resultValue (str): \u5b57\u7b26\u7ed3\u679c\u503c

"},{"location":"qsl4a/system/activityresult/#setresultinteger","title":"setResultInteger()","text":"

\u8bbe\u7f6e\u6574\u6570\u7ed3\u679c\u3002

setResultInteger(resultCode, resultValue)\n

\u53c2\u6570\uff1a - resultCode (int): \u7ed3\u679c\u4ee3\u7801 - resultValue (int): \u6574\u6570\u7ed3\u679c\u503c

"},{"location":"qsl4a/system/activityresult/#setresultlong","title":"setResultLong()","text":"

\u8bbe\u7f6e\u957f\u6574\u6570\u7ed3\u679c\u3002

setResultLong(resultCode, resultValue)\n

\u53c2\u6570\uff1a - resultCode (int): \u7ed3\u679c\u4ee3\u7801 - resultValue (int): \u957f\u6574\u6570\u7ed3\u679c\u503c

"},{"location":"qsl4a/system/activityresult/#setresultfloat","title":"setResultFloat()","text":"

\u8bbe\u7f6e\u6d6e\u70b9\u6570\u7ed3\u679c\u3002

setResultFloat(resultCode, resultValue)\n

\u53c2\u6570\uff1a - resultCode (int): \u7ed3\u679c\u4ee3\u7801 - resultValue (float): \u6d6e\u70b9\u6570\u7ed3\u679c\u503c

"},{"location":"qsl4a/system/activityresult/#setresultdouble","title":"setResultDouble()","text":"

\u8bbe\u7f6e\u53cc\u7cbe\u5ea6\u6d6e\u70b9\u6570\u7ed3\u679c\u3002

setResultDouble(resultCode, resultValue)\n

\u53c2\u6570\uff1a - resultCode (int): \u7ed3\u679c\u4ee3\u7801 - resultValue (float): \u53cc\u7cbe\u5ea6\u6d6e\u70b9\u6570\u7ed3\u679c\u503c

"},{"location":"qsl4a/system/activityresult/#setresultstring","title":"setResultString()","text":"

\u8bbe\u7f6e\u5b57\u7b26\u4e32\u7ed3\u679c\u3002

setResultString(resultCode, resultValue)\n

\u53c2\u6570\uff1a - resultCode (int): \u7ed3\u679c\u4ee3\u7801 - resultValue (str): \u5b57\u7b26\u4e32\u7ed3\u679c\u503c

"},{"location":"qsl4a/system/activityresult/#setresultbooleanarray","title":"setResultBooleanArray()","text":"

\u8bbe\u7f6e\u5e03\u5c14\u6570\u7ec4\u7ed3\u679c\u3002

setResultBooleanArray(resultCode, resultValue)\n

\u53c2\u6570\uff1a - resultCode (int): \u7ed3\u679c\u4ee3\u7801 - resultValue (list): \u5e03\u5c14\u6570\u7ec4

"},{"location":"qsl4a/system/activityresult/#setresultbytearray","title":"setResultByteArray()","text":"

\u8bbe\u7f6e\u5b57\u8282\u6570\u7ec4\u7ed3\u679c\u3002

setResultByteArray(resultCode, resultValue)\n

\u53c2\u6570\uff1a - resultCode (int): \u7ed3\u679c\u4ee3\u7801 - resultValue (list): \u5b57\u8282\u6570\u7ec4

"},{"location":"qsl4a/system/activityresult/#setresultshortarray","title":"setResultShortArray()","text":"

\u8bbe\u7f6e\u77ed\u6574\u6570\u6570\u7ec4\u7ed3\u679c\u3002

setResultShortArray(resultCode, resultValue)\n

\u53c2\u6570\uff1a - resultCode (int): \u7ed3\u679c\u4ee3\u7801 - resultValue (list): \u77ed\u6574\u6570\u6570\u7ec4

"},{"location":"qsl4a/system/activityresult/#setresultchararray","title":"setResultCharArray()","text":"

\u8bbe\u7f6e\u5b57\u7b26\u6570\u7ec4\u7ed3\u679c\u3002

setResultCharArray(resultCode, resultValue)\n

\u53c2\u6570\uff1a - resultCode (int): \u7ed3\u679c\u4ee3\u7801 - resultValue (list): \u5b57\u7b26\u6570\u7ec4

"},{"location":"qsl4a/system/activityresult/#setresultintegerarray","title":"setResultIntegerArray()","text":"

\u8bbe\u7f6e\u6574\u6570\u6570\u7ec4\u7ed3\u679c\u3002

setResultIntegerArray(resultCode, resultValue)\n

\u53c2\u6570\uff1a - resultCode (int): \u7ed3\u679c\u4ee3\u7801 - resultValue (list): \u6574\u6570\u6570\u7ec4

"},{"location":"qsl4a/system/activityresult/#setresultlongarray","title":"setResultLongArray()","text":"

\u8bbe\u7f6e\u957f\u6574\u6570\u6570\u7ec4\u7ed3\u679c\u3002

setResultLongArray(resultCode, resultValue)\n

\u53c2\u6570\uff1a - resultCode (int): \u7ed3\u679c\u4ee3\u7801 - resultValue (list): \u957f\u6574\u6570\u6570\u7ec4

"},{"location":"qsl4a/system/activityresult/#setresultfloatarray","title":"setResultFloatArray()","text":"

\u8bbe\u7f6e\u6d6e\u70b9\u6570\u6570\u7ec4\u7ed3\u679c\u3002

setResultFloatArray(resultCode, resultValue)\n

\u53c2\u6570\uff1a - resultCode (int): \u7ed3\u679c\u4ee3\u7801 - resultValue (list): \u6d6e\u70b9\u6570\u6570\u7ec4

"},{"location":"qsl4a/system/activityresult/#setresultdoublearray","title":"setResultDoubleArray()","text":"

\u8bbe\u7f6e\u53cc\u7cbe\u5ea6\u6d6e\u70b9\u6570\u6570\u7ec4\u7ed3\u679c\u3002

setResultDoubleArray(resultCode, resultValue)\n

\u53c2\u6570\uff1a - resultCode (int): \u7ed3\u679c\u4ee3\u7801 - resultValue (list): \u53cc\u7cbe\u5ea6\u6d6e\u70b9\u6570\u6570\u7ec4

"},{"location":"qsl4a/system/activityresult/#setresultstringarray","title":"setResultStringArray()","text":"

\u8bbe\u7f6e\u5b57\u7b26\u4e32\u6570\u7ec4\u7ed3\u679c\u3002

setResultStringArray(resultCode, resultValue)\n

\u53c2\u6570\uff1a - resultCode (int): \u7ed3\u679c\u4ee3\u7801 - resultValue (list): \u5b57\u7b26\u4e32\u6570\u7ec4

"},{"location":"qsl4a/system/activityresult/#setresultserializable","title":"setResultSerializable()","text":"

\u8bbe\u7f6e\u53ef\u5e8f\u5217\u5316\u7ed3\u679c\u3002

setResultSerializable(resultCode, resultValue)\n

\u53c2\u6570\uff1a - resultCode (int): \u7ed3\u679c\u4ee3\u7801 - resultValue: \u53ef\u5e8f\u5217\u5316\u7ed3\u679c\u503c

"},{"location":"qsl4a/system/activityresult/#_2","title":"\u4f7f\u7528\u793a\u4f8b","text":"
import androidhelper\n\ndroid = androidhelper.Android()\n\n# \u6267\u884c activity \u540e\uff0c\u8bbe\u7f6e\u7ed3\u679c\n# \u793a\u4f8b\uff1a\u8fd4\u56de\u6210\u529f\u53ca\u6570\u636e\ndroid.setResultInteger(0, 200)  # RESULT_OK\ndroid.setResultString(0, \"Operation completed successfully\")\n\n# \u8fd4\u56de\u6570\u7ec4\u7ed3\u679c\ndroid.setResultIntegerArray(0, [1, 2, 3, 4, 5])\n
"},{"location":"qsl4a/system/application/","title":"\u5e94\u7528\u7ba1\u7406","text":"

\u7ba1\u7406\u5e94\u7528\u7a0b\u5e8f\u3001\u542f\u52a8\u5e94\u7528\u548c\u67e5\u8be2\u7cfb\u7edf\u4fe1\u606f\u3002

"},{"location":"qsl4a/system/application/#_2","title":"\u5e94\u7528\u7a0b\u5e8f\u4fe1\u606f","text":""},{"location":"qsl4a/system/application/#getapplicationinfo","title":"getApplicationInfo()","text":"

\u83b7\u53d6\u5e94\u7528\u4fe1\u606f\u3002

getApplicationInfo(packageName=None)\n

\u53c2\u6570\uff1a - packageName (str): \u5305\u540d\uff08None = \u5f53\u524d\u5e94\u7528\uff09

\u8fd4\u56de\uff1a \u5e94\u7528\u4fe1\u606f\u5b57\u5178

"},{"location":"qsl4a/system/application/#getinstalledpackages","title":"getInstalledPackages()","text":"

\u83b7\u53d6\u5df2\u5b89\u88c5\u5305\u5217\u8868\u3002

getInstalledPackages(flag=4)\n

\u53c2\u6570\uff1a - flag (int): \u5305\u6807\u5fd7\u8fc7\u6ee4\u5668\uff08\u9ed8\u8ba4\uff1a4\uff09

\u8fd4\u56de\uff1a \u5df2\u5b89\u88c5\u5305\u5217\u8868

"},{"location":"qsl4a/system/application/#getrunningpackages","title":"getRunningPackages()","text":"

\u5217\u51fa\u6b63\u5728\u8fd0\u884c\u7684\u5305\u3002

getRunningPackages()\n

\u8fd4\u56de\uff1a \u5305\u540d\u5217\u8868

"},{"location":"qsl4a/system/application/#getlaunchablepackages","title":"getLaunchablePackages()","text":"

\u83b7\u53d6\u53ef\u542f\u52a8\u5305\u5217\u8868\u3002

getLaunchablePackages(needClassName=False)\n

\u53c2\u6570\uff1a - needClassName (bool): \u5305\u542b\u4e3b activity \u7c7b\u540d\uff08\u9ed8\u8ba4\uff1aFalse\uff09

\u8fd4\u56de\uff1a \u53ef\u542f\u52a8\u5305\u540d\u5217\u8868\u6216\u5305\u542b\u7c7b\u540d\u7684\u5b57\u5178

"},{"location":"qsl4a/system/application/#_3","title":"\u5e94\u7528\u63a7\u5236","text":""},{"location":"qsl4a/system/application/#launch","title":"launch()","text":"

\u542f\u52a8\u5e94\u7528\u7a0b\u5e8f\u3002

launch(classname=None, packagename=None, wait=True)\n

\u53c2\u6570\uff1a - classname (str): \u4e3b activity \u7c7b\u540d - packagename (str): \u5305\u540d - wait (bool): \u7b49\u5f85\u542f\u52a8\u5b8c\u6210\uff08\u9ed8\u8ba4\uff1aTrue\uff09

\u8fd4\u56de\uff1a \u542f\u52a8\u7ed3\u679c

"},{"location":"qsl4a/system/application/#forcestoppackage","title":"forceStopPackage()","text":"

\u5f3a\u5236\u505c\u6b62\u5e94\u7528\u7a0b\u5e8f\u3002

forceStopPackage(packageName)\n

\u53c2\u6570\uff1a - packageName (str): \u8981\u505c\u6b62\u7684\u5305\u540d

"},{"location":"qsl4a/system/application/#_4","title":"\u7248\u672c\u4fe1\u606f","text":""},{"location":"qsl4a/system/application/#getpackageversion","title":"getPackageVersion()","text":"

\u83b7\u53d6\u5e94\u7528\u7248\u672c\u540d\u79f0\u3002

getPackageVersion(packageName)\n

\u8fd4\u56de\uff1a \u7248\u672c\u5b57\u7b26\u4e32\uff08\u4f8b\u5982 \"3.2.1\"\uff09

"},{"location":"qsl4a/system/application/#getpackageversioncode","title":"getPackageVersionCode()","text":"

\u83b7\u53d6\u5e94\u7528\u7248\u672c\u4ee3\u7801\u3002

getPackageVersionCode(packageName)\n

\u8fd4\u56de\uff1a \u7248\u672c\u4ee3\u7801\u6574\u6570

"},{"location":"qsl4a/system/application/#getconstants","title":"getConstants()","text":"

\u83b7\u53d6\u7c7b\u5e38\u91cf\u3002

getConstants(classname)\n

\u53c2\u6570\uff1a - classname (str): \u5b8c\u6574\u7c7b\u540d

\u8fd4\u56de\uff1a \u5e38\u91cf\u540d\u548c\u503c\u7684\u5b57\u5178

"},{"location":"qsl4a/system/application/#_5","title":"\u7cfb\u7edf\u529f\u80fd","text":""},{"location":"qsl4a/system/application/#backgroundprotect","title":"backgroundProtect()","text":"

\u542f\u7528\u6216\u7981\u7528\u5e94\u7528\u7684\u540e\u53f0\u4fdd\u62a4\u3002

backgroundProtect(enabled=True)\n

\u53c2\u6570\uff1a - enabled (bool): True \u542f\u7528\u4fdd\u62a4\uff0cFalse \u7981\u7528

"},{"location":"qsl4a/system/application/#createscriptshortcut","title":"createScriptShortCut()","text":"

\u4e3a\u811a\u672c\u521b\u5efa\u4e3b\u5c4f\u5e55\u5feb\u6377\u65b9\u5f0f\u3002

createScriptShortCut(scriptPath, label=None, iconPath=None, scriptArg=None)\n

\u53c2\u6570\uff1a - scriptPath (str): Python \u811a\u672c\u8def\u5f84 - label (str, optional): \u5feb\u6377\u65b9\u5f0f\u6807\u7b7e - iconPath (str, optional): \u56fe\u6807\u56fe\u50cf\u8def\u5f84 - scriptArg (str, optional): \u4f20\u9012\u7ed9\u811a\u672c\u7684\u53c2\u6570

"},{"location":"qsl4a/system/application/#_6","title":"\u8bbe\u5907\u4fe1\u606f","text":""},{"location":"qsl4a/system/application/#getandroidid","title":"getAndroidID()","text":"

\u83b7\u53d6 Android \u8bbe\u5907 ID\u3002

getAndroidID()\n

\u8fd4\u56de\uff1a \u552f\u4e00\u7684 Android \u8bbe\u5907 ID \u5b57\u7b26\u4e32

"},{"location":"qsl4a/system/application/#getsysinfo","title":"getSysInfo()","text":"

\u83b7\u53d6\u7cfb\u7edf\u4fe1\u606f\u3002

getSysInfo()\n

\u8fd4\u56de\uff1a \u5305\u542b\u7cfb\u7edf\u8be6\u60c5\u7684\u5b57\u5178

"},{"location":"qsl4a/system/application/#getlocale","title":"getLocale()","text":"

\u83b7\u53d6\u8bbe\u5907\u8bed\u8a00\u73af\u5883\u3002

getLocale()\n

\u8fd4\u56de\uff1a \u8bed\u8a00\u73af\u5883\u5b57\u7b26\u4e32\uff08\u4f8b\u5982 \"en_US\"\uff09

"},{"location":"qsl4a/system/application/#getharmonyosinformation","title":"getHarmonyOsInformation()","text":"

\u5982\u679c\u5728 HarmonyOS \u4e0a\u8fd0\u884c\uff0c\u83b7\u53d6 HarmonyOS \u4fe1\u606f\u3002

getHarmonyOsInformation()\n

\u8fd4\u56de\uff1a HarmonyOS \u7248\u672c\u4fe1\u606f\u6216 None

"},{"location":"qsl4a/system/application/#isexternalstoragemanager","title":"isExternalStorageManager()","text":"

\u68c0\u67e5\u5e94\u7528\u662f\u5426\u6709\u5916\u90e8\u5b58\u50a8\u7ba1\u7406\u5668\u6743\u9650\u3002

isExternalStorageManager()\n

\u8fd4\u56de\uff1a \u5982\u679c\u6709\u6743\u9650\u5219\u4e3a True

"},{"location":"qsl4a/system/application/#_7","title":"\u5185\u5b58\u548c\u663e\u793a","text":""},{"location":"qsl4a/system/application/#getmemoryinfo","title":"getMemoryInfo()","text":"

\u83b7\u53d6\u5185\u5b58\u4fe1\u606f\u3002

getMemoryInfo()\n

\u8fd4\u56de\uff1a \u5305\u542b\u5185\u5b58\u7edf\u8ba1\u7684\u5b57\u5178

"},{"location":"qsl4a/system/application/#getscreeninfo","title":"getScreenInfo()","text":"

\u83b7\u53d6\u5c4f\u5e55\u4fe1\u606f\u3002

getScreenInfo()\n

\u8fd4\u56de\uff1a \u5305\u542b\u5bbd\u5ea6\u3001\u9ad8\u5ea6\u3001\u5bc6\u5ea6\u7684\u5b57\u5178

"},{"location":"qsl4a/system/application/#_8","title":"\u6743\u9650","text":""},{"location":"qsl4a/system/application/#checkpermissions","title":"checkPermissions()","text":"

\u68c0\u67e5\u5f53\u524d\u5e94\u7528\u6743\u9650\u3002

checkPermissions()\n

\u8fd4\u56de\uff1a \u6743\u9650\u53ca\u5176\u72b6\u6001\u7684\u5b57\u5178

"},{"location":"qsl4a/system/application/#requestpermissions","title":"requestPermissions()","text":"

\u5411\u7528\u6237\u8bf7\u6c42\u6743\u9650\u3002

requestPermissions(permissions=None)\n

\u53c2\u6570\uff1a - permissions (list, optional): \u8981\u8bf7\u6c42\u7684\u6743\u9650\u5217\u8868\u3002\u5982\u679c\u4e3a None\uff0c\u5219\u8bf7\u6c42\u6240\u6709\u9700\u8981\u7684\u6743\u9650\u3002

\u8fd4\u56de\uff1a \u6743\u9650\u8bf7\u6c42\u7ed3\u679c

"},{"location":"qsl4a/system/application/#_9","title":"\u7cfb\u7edf\u64cd\u4f5c","text":""},{"location":"qsl4a/system/application/#showscreenlock","title":"showScreenLock()","text":"

\u663e\u793a\u5c4f\u5e55\u9501\u5b9a\uff08PIN/\u56fe\u6848/\u5bc6\u7801\u8f93\u5165\uff09\u3002

showScreenLock()\n
"},{"location":"qsl4a/system/application/#_10","title":"\u4f7f\u7528\u793a\u4f8b","text":"
import androidhelper\n\ndroid = androidhelper.Android()\n\n# \u83b7\u53d6\u7cfb\u7edf\u4fe1\u606f\ninfo = droid.getSysInfo().result\nprint(f\"System: {info}\")\n\n# \u83b7\u53d6\u5e94\u7528\u7248\u672c\nversion = droid.getPackageVersion(\"org.qpython.qpy\").result\nprint(f\"QPython version: {version}\")\n\n# \u5217\u51fa\u5df2\u5b89\u88c5\u5e94\u7528\napps = droid.getInstalledPackages().result\nprint(f\"Installed apps: {len(apps)}\")\n\n# \u542f\u52a8\u5e94\u7528\ndroid.launch(packagename=\"com.android.settings\")\n\n# \u68c0\u67e5\u6743\u9650\nperms = droid.checkPermissions().result\nprint(f\"Permissions: {perms}\")\n\n# \u8bf7\u6c42\u5b58\u50a8\u6743\u9650\ndroid.requestPermissions([\"android.permission.READ_EXTERNAL_STORAGE\"])\n\n# \u521b\u5efa\u5feb\u6377\u65b9\u5f0f\ndroid.createScriptShortCut(\n    \"/sdcard/my_script.py\",\n    label=\"My Script\",\n    iconPath=\"/sdcard/icon.png\"\n)\n
"},{"location":"qsl4a/system/battery/","title":"\u7535\u6c60 API","text":"

\u76d1\u63a7\u8bbe\u5907\u7535\u6c60\u72b6\u6001\u548c\u5065\u5eb7\u72b6\u51b5\u3002

"},{"location":"qsl4a/system/battery/#_1","title":"\u65b9\u6cd5","text":""},{"location":"qsl4a/system/battery/#readbatterydata","title":"readBatteryData()","text":"

\u83b7\u53d6\u5b8c\u6574\u7684\u7535\u6c60\u4fe1\u606f\u3002

readBatteryData()\n

\u8fd4\u56de\uff1a \u5305\u542b\u7535\u6c60\u6570\u636e\u7684\u5b57\u5178

"},{"location":"qsl4a/system/battery/#batterystartmonitoring","title":"batteryStartMonitoring()","text":"

\u5f00\u59cb\u7535\u6c60\u76d1\u63a7\u3002

batteryStartMonitoring()\n
"},{"location":"qsl4a/system/battery/#batterystopmonitoring","title":"batteryStopMonitoring()","text":"

\u505c\u6b62\u7535\u6c60\u76d1\u63a7\u3002

batteryStopMonitoring()\n
"},{"location":"qsl4a/system/battery/#batterygetlevel","title":"batteryGetLevel()","text":"

\u83b7\u53d6\u7535\u6c60\u767e\u5206\u6bd4\u3002

batteryGetLevel()\n

\u8fd4\u56de\uff1a \u6574\u6570\uff080-100\uff09

"},{"location":"qsl4a/system/battery/#batterygetstatus","title":"batteryGetStatus()","text":"

\u83b7\u53d6\u5145\u7535\u72b6\u6001\u3002

batteryGetStatus()\n

\u8fd4\u56de\uff1a - 1: \u672a\u77e5 - 2: \u5145\u7535\u4e2d - 3: \u653e\u7535\u4e2d - 4: \u672a\u5145\u7535 - 5: \u5df2\u5145\u6ee1

"},{"location":"qsl4a/system/battery/#batterygetplugtype","title":"batteryGetPlugType()","text":"

\u83b7\u53d6\u7535\u6e90\u7c7b\u578b\u3002

batteryGetPlugType()\n

\u8fd4\u56de\uff1a - -1: \u672a\u77e5 - 0: \u672a\u63d2\u7535 - 1: AC \u5145\u7535\u5668 - 2: USB \u7aef\u53e3

"},{"location":"qsl4a/system/battery/#batterygethealth","title":"batteryGetHealth()","text":"

\u83b7\u53d6\u7535\u6c60\u5065\u5eb7\u72b6\u51b5\u3002

batteryGetHealth()\n

\u8fd4\u56de\uff1a - 1: \u672a\u77e5 - 2: \u826f\u597d - 3: \u8fc7\u70ed - 4: \u635f\u574f - 5: \u8fc7\u538b - 6: \u672a\u6307\u5b9a\u7684\u6545\u969c

"},{"location":"qsl4a/system/battery/#_2","title":"\u4f7f\u7528\u793a\u4f8b","text":"
import androidhelper\n\ndroid = androidhelper.Android()\n\n# \u83b7\u53d6\u7535\u6c60\u7535\u91cf\nlevel = droid.batteryGetLevel().result\nprint(f\"Battery: {level}%\")\n
"},{"location":"qsl4a/system/qpyinterface/","title":"QPython \u63a5\u53e3 API","text":"

\u4ece\u5176\u4ed6\u5e94\u7528\u6267\u884c QPython \u811a\u672c\u548c\u7ba1\u7406\u5171\u4eab\u53d8\u91cf\u3002

"},{"location":"qsl4a/system/qpyinterface/#_1","title":"\u811a\u672c\u6267\u884c\u65b9\u6cd5","text":""},{"location":"qsl4a/system/qpyinterface/#executeqpy","title":"executeQPy()","text":"

\u6267\u884c QPython \u811a\u672c\u3002

executeQPy(path=\"\", arg=None)\n

\u53c2\u6570\uff1a - path (str): \u811a\u672c\u6587\u4ef6\u8def\u5f84 - arg (str, optional): \u547d\u4ee4\u884c\u53c2\u6570

\u8fd4\u56de\uff1a \u5982\u679c\u542f\u52a8\u6210\u529f\u5219\u4e3a True

"},{"location":"qsl4a/system/qpyinterface/#executeqpyassrv","title":"executeQPyAsSrv()","text":"

\u4f5c\u4e3a\u670d\u52a1\u6267\u884c QPython \u811a\u672c\u3002

executeQPyAsSrv(path=None)\n

\u53c2\u6570\uff1a - path (str, optional): \u811a\u672c\u6587\u4ef6\u8def\u5f84

\u8fd4\u56de\uff1a \u5982\u679c\u542f\u52a8\u6210\u529f\u5219\u4e3a True

"},{"location":"qsl4a/system/qpyinterface/#executeqpycode","title":"executeQPyCode()","text":"

\u76f4\u63a5\u6267\u884c Python \u4ee3\u7801\u3002

executeQPyCode(code=None)\n

\u53c2\u6570\uff1a - code (str, optional): \u8981\u6267\u884c\u7684 Python \u4ee3\u7801

\u8fd4\u56de\uff1a \u5982\u679c\u542f\u52a8\u6210\u529f\u5219\u4e3a True

"},{"location":"qsl4a/system/qpyinterface/#executeqpycodeassrv","title":"executeQPyCodeAsSrv()","text":"

\u4f5c\u4e3a\u670d\u52a1\u6267\u884c Python \u4ee3\u7801\u3002

executeQPyCodeAsSrv(code=None)\n

\u53c2\u6570\uff1a - code (str, optional): \u8981\u6267\u884c\u7684 Python \u4ee3\u7801

\u8fd4\u56de\uff1a \u5982\u679c\u542f\u52a8\u6210\u529f\u5219\u4e3a True

"},{"location":"qsl4a/system/qpyinterface/#_2","title":"\u5171\u4eab\u53d8\u91cf","text":"

\u5171\u4eab\u53d8\u91cf\u5141\u8bb8 QPython \u4e0e\u5176\u4ed6\u5e94\u7528\u4e4b\u95f4\u8fdb\u884c\u901a\u4fe1\u3002

"},{"location":"qsl4a/system/qpyinterface/#sharedvariableset","title":"sharedVariableSet()","text":"

\u8bbe\u7f6e Java \u5171\u4eab\u53d8\u91cf\u3002

sharedVariableSet(key, value)\n

\u53c2\u6570\uff1a - key (str): \u53d8\u91cf\u540d - value (str): \u53d8\u91cf\u503c

\u8fd4\u56de\uff1a \u5b58\u50a8\u7684\u503c

"},{"location":"qsl4a/system/qpyinterface/#sharedvariableget","title":"sharedVariableGet()","text":"

\u83b7\u53d6 Java \u5171\u4eab\u53d8\u91cf\u3002

sharedVariableGet(key)\n

\u53c2\u6570\uff1a - key (str): \u53d8\u91cf\u540d

\u8fd4\u56de\uff1a \u5b58\u50a8\u7684\u503c

"},{"location":"qsl4a/system/qpyinterface/#sharedvariableremove","title":"sharedVariableRemove()","text":"

\u79fb\u9664 Java \u5171\u4eab\u53d8\u91cf\u3002

sharedVariableRemove(key)\n

\u53c2\u6570\uff1a - key (str): \u8981\u79fb\u9664\u7684\u53d8\u91cf\u540d

\u8fd4\u56de\uff1a \u88ab\u79fb\u9664\u7684\u503c

"},{"location":"qsl4a/system/qpyinterface/#getlastlog","title":"getLastLog()","text":"

\u83b7\u53d6 QPython \u7684\u6700\u540e\u65e5\u5fd7\u8f93\u51fa\u3002

getLastLog()\n

\u8fd4\u56de\uff1a \u5b57\u7b26\u4e32\u65e5\u5fd7\u5185\u5bb9

"},{"location":"qsl4a/system/qpyinterface/#_3","title":"\u4f7f\u7528\u793a\u4f8b","text":"
import androidhelper\n\ndroid = androidhelper.Android()\n\n# \u6267\u884c\u811a\u672c\ndroid.executeQPy(\"/sdcard/my_script.py\", arg=\"test\")\n\n# \u76f4\u63a5\u6267\u884c\u4ee3\u7801\ncode = \"print('Hello from QPython!')\"\ndroid.executeQPyCode(code)\n\n# \u4f7f\u7528\u5171\u4eab\u53d8\u91cf\ndroid.sharedVariableSet(\"username\", \"alice\")\nusername = droid.sharedVariableGet(\"username\").result\nprint(f\"Username: {username}\")\n\n# \u79fb\u9664\u53d8\u91cf\ndroid.sharedVariableRemove(\"username\")\n\n# \u83b7\u53d6\u6700\u8fd1\u65e5\u5fd7\nlog = droid.getLastLog().result\nprint(f\"Log: {log}\")\n
"},{"location":"qsl4a/system/sensors/","title":"\u4f20\u611f\u5668 API","text":"

\u8bbf\u95ee\u8bbe\u5907\u4f20\u611f\u5668\uff0c\u5305\u62ec\u52a0\u901f\u5ea6\u8ba1\u3001\u9640\u87ba\u4eea\u3001\u78c1\u529b\u8ba1\u7b49\u3002

"},{"location":"qsl4a/system/sensors/#_1","title":"\u65b9\u6cd5","text":""},{"location":"qsl4a/system/sensors/#startsensingtimed","title":"startSensingTimed()","text":"

\u5f00\u59cb\u4f20\u611f\u5668\u76d1\u63a7\u5e76\u8bbe\u7f6e\u65f6\u95f4\u95f4\u9694\u3002

startSensingTimed(sensorNumber, delayTime)\n

\u53c2\u6570\uff1a - sensorNumber (int): \u4f20\u611f\u5668 ID\uff08\u901a\u5e38\u4e3a 1-3\uff09 - delayTime (int): \u8bfb\u53d6\u4e4b\u95f4\u7684\u5ef6\u8fdf\uff08\u6beb\u79d2\uff09

"},{"location":"qsl4a/system/sensors/#startsensingthreshold","title":"startSensingThreshold()","text":"

\u5f00\u59cb\u5e26\u9608\u503c\u89e6\u53d1\u7684\u4f20\u611f\u5668\u76d1\u63a7\u3002

startSensingThreshold(sensorNumber, threshold, axis)\n

\u53c2\u6570\uff1a - sensorNumber (int): \u4f20\u611f\u5668 ID - threshold (float): \u89e6\u53d1\u9608\u503c - axis (int): \u8981\u76d1\u63a7\u7684\u8f74\uff080=X\uff0c1=Y\uff0c2=Z\uff09

"},{"location":"qsl4a/system/sensors/#stopsensing","title":"stopSensing()","text":"

\u505c\u6b62\u6240\u6709\u4f20\u611f\u5668\u76d1\u63a7\u3002

stopSensing()\n
"},{"location":"qsl4a/system/sensors/#readsensors","title":"readSensors()","text":"

\u8bfb\u53d6\u5f53\u524d\u4f20\u611f\u5668\u6570\u636e\u3002

readSensors()\n

\u8fd4\u56de\uff1a \u4f20\u611f\u5668\u6570\u636e\u5b57\u5178

"},{"location":"qsl4a/system/sensors/#sensorsreadaccelerometer","title":"sensorsReadAccelerometer()","text":"

\u8bfb\u53d6\u52a0\u901f\u5ea6\u8ba1\u503c\u3002

sensorsReadAccelerometer()\n

\u8fd4\u56de\uff1a \u5217\u8868 [X, Y, Z]\uff0c\u5355\u4f4d m/s\u00b2

"},{"location":"qsl4a/system/sensors/#sensorsreadgyroscope","title":"sensorsReadGyroscope()","text":"

\u8bfb\u53d6\u9640\u87ba\u4eea\u503c\u3002

sensorsReadGyroscope()\n

\u8fd4\u56de\uff1a \u5217\u8868 [X, Y, Z]\uff0c\u5355\u4f4d rad/s

"},{"location":"qsl4a/system/sensors/#sensorsreadmagnetometer","title":"sensorsReadMagnetometer()","text":"

\u8bfb\u53d6\u78c1\u573a\u503c\u3002

sensorsReadMagnetometer()\n

\u8fd4\u56de\uff1a \u5217\u8868 [X, Y, Z]\uff0c\u5355\u4f4d \u03bcT

"},{"location":"qsl4a/system/sensors/#sensorsreadorientation","title":"sensorsReadOrientation()","text":"

\u8bfb\u53d6\u8bbe\u5907\u65b9\u5411\u3002

sensorsReadOrientation()\n

\u8fd4\u56de\uff1a \u5217\u8868 [azimuth, pitch, roll]\uff0c\u5355\u4f4d\u5ea6

"},{"location":"qsl4a/system/sensors/#sensorsgetlight","title":"sensorsGetLight()","text":"

\u8bfb\u53d6\u5149\u4f20\u611f\u5668\u503c\u3002

sensorsGetLight()\n

\u8fd4\u56de\uff1a \u5149\u7ea7\u522b\uff08lux\uff09

"},{"location":"qsl4a/system/sensors/#sensorsgetstepcounter","title":"sensorsGetStepCounter()","text":"

\u8bfb\u53d6\u6b65\u8ba1\u6570\u5668\u3002

sensorsGetStepCounter()\n

\u8fd4\u56de\uff1a \u6b65\u6570

"},{"location":"qsl4a/system/sensors/#sensorsgetaccuracy","title":"sensorsGetAccuracy()","text":"

\u83b7\u53d6\u5f53\u524d\u4f20\u611f\u5668\u7cbe\u5ea6\u3002

sensorsGetAccuracy()\n

\u8fd4\u56de\uff1a \u7cbe\u5ea6\u503c\uff080-3\uff1aUNRELIABLE\u3001ACCURACY_LOW\u3001ACCURACY_MEDIUM\u3001ACCURACY_HIGH\uff09

"},{"location":"qsl4a/system/sensors/#_2","title":"\u4f7f\u7528\u793a\u4f8b","text":"
import androidhelper\nimport time\n\ndroid = androidhelper.Android()\n\n# \u5f00\u59cb\u611f\u77e5\ndroid.startSensingTimed(1, 250)\n\n# \u8bfb\u53d6\u4f20\u611f\u5668 10 \u6b21\nfor i in range(10):\n    accel = droid.sensorsReadAccelerometer().result\n    print(f\"Accel: X={accel[0]:.2f}, Y={accel[1]:.2f}, Z={accel[2]:.2f}\")\n    time.sleep(0.5)\n\ndroid.stopSensing()\n
"},{"location":"qsl4a/system/settings/","title":"\u8bbe\u7f6e API","text":"

\u63a7\u5236\u7cfb\u7edf\u8bbe\u7f6e\uff0c\u5305\u62ec\u5c4f\u5e55\u3001\u58f0\u97f3\u548c\u7f51\u7edc\u8bbe\u7f6e\u3002

"},{"location":"qsl4a/system/settings/#_1","title":"\u5c4f\u5e55\u8bbe\u7f6e","text":""},{"location":"qsl4a/system/settings/#setscreentimeout","title":"setScreenTimeout()","text":"

\u8bbe\u7f6e\u5c4f\u5e55\u8d85\u65f6\u503c\u3002

setScreenTimeout(value)\n

\u53c2\u6570\uff1a - value (int): \u5c4f\u5e55\u8d85\u65f6\u65f6\u95f4\uff08\u79d2\uff09

\u8fd4\u56de\uff1a \u4e4b\u524d\u7684\u8d85\u65f6\u503c

"},{"location":"qsl4a/system/settings/#getscreentimeout","title":"getScreenTimeout()","text":"

\u83b7\u53d6\u5f53\u524d\u5c4f\u5e55\u8d85\u65f6\u3002

getScreenTimeout()\n

\u8fd4\u56de\uff1a \u5f53\u524d\u5c4f\u5e55\u8d85\u65f6\u65f6\u95f4\uff08\u79d2\uff09

"},{"location":"qsl4a/system/settings/#getscreenbrightness","title":"getScreenBrightness()","text":"

\u83b7\u53d6\u5c4f\u5e55\u4eae\u5ea6\u503c\u3002

getScreenBrightness()\n

\u8fd4\u56de\uff1a \u4eae\u5ea6\u503c\uff080-255\uff09

"},{"location":"qsl4a/system/settings/#setscreenbrightness","title":"setScreenBrightness()","text":"

\u8bbe\u7f6e\u5c4f\u5e55\u4eae\u5ea6\u3002

setScreenBrightness(value=None)\n

\u53c2\u6570\uff1a - value (int, optional): \u4eae\u5ea6\u503c\uff080-255\uff09\uff0c\u6216 None \u8868\u793a\u81ea\u52a8

\u8fd4\u56de\uff1a \u4e4b\u524d\u7684\u4eae\u5ea6\u503c

"},{"location":"qsl4a/system/settings/#checkscreenon","title":"checkScreenOn()","text":"

\u68c0\u67e5\u5c4f\u5e55\u662f\u5426\u4eae\u7740\u3002

checkScreenOn()\n

\u8fd4\u56de\uff1a \u5982\u679c\u5c4f\u5e55\u4eae\u7740\u5219\u4e3a True\uff0c\u5426\u5219\u4e3a False

"},{"location":"qsl4a/system/settings/#_2","title":"\u98de\u884c\u6a21\u5f0f","text":""},{"location":"qsl4a/system/settings/#checkairplanemode","title":"checkAirplaneMode()","text":"

\u68c0\u67e5\u98de\u884c\u6a21\u5f0f\u662f\u5426\u542f\u7528\u3002

checkAirplaneMode()\n

\u8fd4\u56de\uff1a \u5982\u679c\u98de\u884c\u6a21\u5f0f\u5f00\u7740\u5219\u4e3a True

"},{"location":"qsl4a/system/settings/#_3","title":"\u94c3\u58f0\u8bbe\u7f6e","text":""},{"location":"qsl4a/system/settings/#checkringersilentmode","title":"checkRingerSilentMode()","text":"

\u68c0\u67e5\u94c3\u58f0\u662f\u5426\u5904\u4e8e\u9759\u97f3\u6a21\u5f0f\u3002

checkRingerSilentMode()\n

\u8fd4\u56de\uff1a \u5982\u679c\u9759\u97f3\u6a21\u5f0f\u5f00\u7740\u5219\u4e3a True

"},{"location":"qsl4a/system/settings/#toggleringersilentmode","title":"toggleRingerSilentMode()","text":"

\u5207\u6362\u94c3\u58f0\u9759\u97f3\u6a21\u5f0f\u3002

toggleRingerSilentMode(enabled=None)\n

\u53c2\u6570\uff1a - enabled (bool, optional): True \u542f\u7528\uff0cFalse \u7981\u7528\uff0cNone \u5207\u6362

\u8fd4\u56de\uff1a \u65b0\u72b6\u6001

"},{"location":"qsl4a/system/settings/#togglevibratemode","title":"toggleVibrateMode()","text":"

\u5207\u6362\u632f\u52a8\u6a21\u5f0f\u3002

toggleVibrateMode(enabled=None, ringer=None)\n

\u53c2\u6570\uff1a - enabled (bool, optional): \u5207\u6362\u632f\u52a8\u5f00/\u5173 - ringer (bool, optional): \u5e94\u7528\u4e8e\u94c3\u58f0\u6a21\u5f0f

\u8fd4\u56de\uff1a \u65b0\u72b6\u6001

"},{"location":"qsl4a/system/settings/#getvibratemode","title":"getVibrateMode()","text":"

\u83b7\u53d6\u632f\u52a8\u6a21\u5f0f\u8bbe\u7f6e\u3002

getVibrateMode(ringer=None)\n

\u53c2\u6570\uff1a - ringer (bool, optional): \u68c0\u67e5\u94c3\u58f0\u632f\u52a8\u6a21\u5f0f

\u8fd4\u56de\uff1a \u5982\u679c\u632f\u52a8\u5df2\u542f\u7528\u5219\u4e3a True

"},{"location":"qsl4a/system/settings/#_4","title":"\u97f3\u91cf\u8bbe\u7f6e","text":""},{"location":"qsl4a/system/settings/#getringervolume","title":"getRingerVolume()","text":"

\u83b7\u53d6\u5f53\u524d\u94c3\u58f0\u97f3\u91cf\u3002

getRingerVolume()\n

\u8fd4\u56de\uff1a \u94c3\u58f0\u97f3\u91cf\u7ea7\u522b\uff08\u901a\u5e38\u4e3a 0-7\uff09

"},{"location":"qsl4a/system/settings/#getmaxringervolume","title":"getMaxRingerVolume()","text":"

\u83b7\u53d6\u6700\u5927\u94c3\u58f0\u97f3\u91cf\u3002

getMaxRingerVolume()\n

\u8fd4\u56de\uff1a \u6700\u5927\u94c3\u58f0\u97f3\u91cf

"},{"location":"qsl4a/system/settings/#setringervolume","title":"setRingerVolume()","text":"

\u8bbe\u7f6e\u94c3\u58f0\u97f3\u91cf\u3002

setRingerVolume(volume)\n

\u53c2\u6570\uff1a - volume (int): \u97f3\u91cf\u7ea7\u522b

"},{"location":"qsl4a/system/settings/#getmediavolume","title":"getMediaVolume()","text":"

\u83b7\u53d6\u5f53\u524d\u5a92\u4f53\u97f3\u91cf\u3002

getMediaVolume()\n

\u8fd4\u56de\uff1a \u5a92\u4f53\u97f3\u91cf\u7ea7\u522b\uff08\u901a\u5e38\u4e3a 0-15\uff09

"},{"location":"qsl4a/system/settings/#getmaxmediavolume","title":"getMaxMediaVolume()","text":"

\u83b7\u53d6\u6700\u5927\u5a92\u4f53\u97f3\u91cf\u3002

getMaxMediaVolume()\n

\u8fd4\u56de\uff1a \u6700\u5927\u5a92\u4f53\u97f3\u91cf

"},{"location":"qsl4a/system/settings/#setmediavolume","title":"setMediaVolume()","text":"

\u8bbe\u7f6e\u5a92\u4f53\u97f3\u91cf\u3002

setMediaVolume(volume)\n

\u53c2\u6570\uff1a - volume (int): \u97f3\u91cf\u7ea7\u522b

"},{"location":"qsl4a/system/settings/#_5","title":"\u7cfb\u7edf\u4fe1\u606f","text":""},{"location":"qsl4a/system/settings/#elapsedrealtimenanos","title":"elapsedRealtimeNanos()","text":"

\u83b7\u53d6\u81ea\u7cfb\u7edf\u542f\u52a8\u4ee5\u6765\u7684\u7eb3\u79d2\u6570\u3002

elapsedRealtimeNanos()\n

\u8fd4\u56de\uff1a \u7eb3\u79d2\u6570\uff08\u53ef\u7528\u4e8e\u8ba1\u65f6\uff09

"},{"location":"qsl4a/system/settings/#gettrafficstats","title":"getTrafficStats()","text":"

\u83b7\u53d6\u7f51\u7edc\u6d41\u91cf\u7edf\u8ba1\u3002

getTrafficStats(flags=7)\n

\u53c2\u6570\uff1a - flags (int): \u8981\u68c0\u7d22\u7684\u7edf\u8ba1\uff08\u9ed8\u8ba4\uff1a7 = \u5168\u90e8\uff09

\u8fd4\u56de\uff1a \u5305\u542b\u53d1\u9001/\u63a5\u6536\u5b57\u8282\u7684\u5b57\u5178

"},{"location":"qsl4a/system/settings/#getapptxbytes","title":"getAppTxBytes()","text":"

\u83b7\u53d6 QPython \u5e94\u7528\u7684\u53d1\u9001\u5b57\u8282\u3002

getAppTxBytes(packageName)\n

\u53c2\u6570\uff1a - packageName (str): \u5305\u540d

\u8fd4\u56de\uff1a \u5305\u542b tx/rx \u5b57\u8282\u7684\u5b57\u5178

"},{"location":"qsl4a/system/settings/#_6","title":"\u4f7f\u7528\u793a\u4f8b","text":"
import androidhelper\n\ndroid = androidhelper.Android()\n\n# \u5c4f\u5e55\u8bbe\u7f6e\ncurrent_timeout = droid.getScreenTimeout().result\nprint(f\"Current timeout: {current_timeout}s\")\ndroid.setScreenTimeout(30)\n\n# \u68c0\u67e5\u5c4f\u5e55\nif droid.checkScreenOn().result:\n    print(\"Screen is on\")\n\n# \u97f3\u91cf\u63a7\u5236\nmedia_vol = droid.getMediaVolume().result\nprint(f\"Media volume: {media_vol}\")\ndroid.setMediaVolume(10)\n\n# \u68c0\u67e5\u98de\u884c\u6a21\u5f0f\nif droid.checkAirplaneMode().result:\n    print(\"Airplane mode is on\")\n
"},{"location":"qsl4a/system/sysinfo/","title":"\u7cfb\u7edf\u4fe1\u606f","text":"

\u68c0\u7d22\u8bbe\u5907\u548c\u7cfb\u7edf\u4fe1\u606f\u3002

"},{"location":"qsl4a/system/sysinfo/#_2","title":"\u8bbe\u5907\u4fe1\u606f","text":""},{"location":"qsl4a/system/sysinfo/#getandroidid","title":"getAndroidID()","text":"

\u83b7\u53d6 Android \u8bbe\u5907 ID\u3002

getAndroidID()\n

\u8fd4\u56de\uff1a \u5b57\u7b26\u4e32\u8bbe\u5907 ID

"},{"location":"qsl4a/system/sysinfo/#getsysinfo","title":"getSysInfo()","text":"

\u83b7\u53d6\u7efc\u5408\u7cfb\u7edf\u4fe1\u606f\u3002

getSysInfo()\n

\u8fd4\u56de\uff1a \u5305\u542b\u7cfb\u7edf\u8be6\u60c5\u7684\u5b57\u5178

"},{"location":"qsl4a/system/sysinfo/#getlocale","title":"getLocale()","text":"

\u83b7\u53d6\u8bbe\u5907\u8bed\u8a00\u73af\u5883\u8bbe\u7f6e\u3002

getLocale()\n

\u8fd4\u56de\uff1a \u8bed\u8a00\u73af\u5883\u5b57\u7b26\u4e32\uff08\u4f8b\u5982 \"en_US\"\uff09

"},{"location":"qsl4a/system/sysinfo/#_3","title":"\u5185\u5b58","text":""},{"location":"qsl4a/system/sysinfo/#getmemoryinfo","title":"getMemoryInfo()","text":"

\u83b7\u53d6 RAM \u4fe1\u606f\u3002

getMemoryInfo()\n

\u8fd4\u56de\uff1a \u5305\u542b\u5185\u5b58\u7edf\u8ba1\u7684\u5b57\u5178

"},{"location":"qsl4a/system/sysinfo/#_4","title":"\u663e\u793a","text":""},{"location":"qsl4a/system/sysinfo/#getscreeninfo","title":"getScreenInfo()","text":"

\u83b7\u53d6\u663e\u793a\u4fe1\u606f\u3002

getScreenInfo()\n

\u8fd4\u56de\uff1a \u5305\u542b\u5bbd\u5ea6\u3001\u9ad8\u5ea6\u3001\u5bc6\u5ea6\u7684\u5b57\u5178

"},{"location":"qsl4a/system/sysinfo/#_5","title":"\u6807\u8bc6\u7b26","text":""},{"location":"qsl4a/system/sysinfo/#getimei","title":"getImei()","text":"

\u83b7\u53d6\u8bbe\u5907 IMEI\u3002

getImei(slotIndex=None)\n
"},{"location":"qsl4a/system/sysinfo/#getmeid","title":"getMeid()","text":"

\u83b7\u53d6\u8bbe\u5907 MEID\u3002

getMeid(slotIndex=None)\n
"},{"location":"qsl4a/system/sysinfo/#_6","title":"\u4f7f\u7528\u793a\u4f8b","text":"
import androidhelper\n\ndroid = androidhelper.Android()\n\n# \u8bbe\u5907 ID\nandroid_id = droid.getAndroidID().result\nprint(f\"Android ID: {android_id}\")\n\n# \u5c4f\u5e55\u4fe1\u606f\nscreen = droid.getScreenInfo().result\nprint(f\"Screen: {screen['width']}x{screen['height']}\")\n\n# \u5185\u5b58\nmemory = droid.getMemoryInfo().result\nprint(f\"Memory: {memory}\")\n
"},{"location":"qsl4a/system/wakelock/","title":"WakeLock API","text":"

\u63a7\u5236\u8bbe\u5907\u5524\u9192\u9501\u4ee5\u4fdd\u6301 CPU \u6216\u5c4f\u5e55\u5f00\u542f\u3002

"},{"location":"qsl4a/system/wakelock/#wake-lock","title":"Wake Lock \u7c7b\u578b","text":"

QSL4A \u63d0\u4f9b\u4e0d\u540c\u7c7b\u578b\u7684\u5524\u9192\u9501\uff1a

\u7c7b\u578b \u63cf\u8ff0 Full CPU \u5f00\u542f\uff0c\u5c4f\u5e55\u4eae\uff0c\u952e\u76d8\u4eae Partial \u4ec5 CPU \u5f00\u542f Bright CPU \u5f00\u542f\uff0c\u5c4f\u5e55\u4eae Dim CPU \u5f00\u542f\uff0c\u5c4f\u5e55\u6697"},{"location":"qsl4a/system/wakelock/#wake-lock_1","title":"Wake Lock \u65b9\u6cd5","text":""},{"location":"qsl4a/system/wakelock/#wakelockacquirefull","title":"wakeLockAcquireFull()","text":"

\u83b7\u53d6\u5b8c\u6574\u5524\u9192\u9501\uff08CPU \u5f00\u542f\uff0c\u5c4f\u5e55\u4eae\uff0c\u952e\u76d8\u4eae\uff09\u3002

wakeLockAcquireFull()\n
"},{"location":"qsl4a/system/wakelock/#wakelockacquirepartial","title":"wakeLockAcquirePartial()","text":"

\u83b7\u53d6\u90e8\u5206\u5524\u9192\u9501\uff08\u4ec5 CPU \u5f00\u542f\uff09\u3002

wakeLockAcquirePartial()\n
"},{"location":"qsl4a/system/wakelock/#wakelockacquirebright","title":"wakeLockAcquireBright()","text":"

\u83b7\u53d6\u660e\u4eae\u5524\u9192\u9501\uff08CPU \u5f00\u542f\uff0c\u5c4f\u5e55\u4eae\uff09\u3002

wakeLockAcquireBright()\n
"},{"location":"qsl4a/system/wakelock/#wakelockacquiredim","title":"wakeLockAcquireDim()","text":"

\u83b7\u53d6\u6697\u6de1\u5524\u9192\u9501\uff08CPU \u5f00\u542f\uff0c\u5c4f\u5e55\u6697\uff09\u3002

wakeLockAcquireDim()\n
"},{"location":"qsl4a/system/wakelock/#wakelockrelease","title":"wakeLockRelease()","text":"

\u91ca\u653e\u5524\u9192\u9501\u3002

wakeLockRelease()\n
"},{"location":"qsl4a/system/wakelock/#_1","title":"\u4f7f\u7528\u793a\u4f8b","text":"
import androidhelper\nimport time\n\ndroid = androidhelper.Android()\n\n# \u83b7\u53d6\u5b8c\u6574\u5524\u9192\u9501\ndroid.wakeLockAcquireFull()\n\n# \u5728\u4fdd\u6301\u5c4f\u5e55\u5f00\u542f\u65f6\u6267\u884c\u91cd\u8981\u5de5\u4f5c\nprint(\"Screen will stay on\")\ntime.sleep(10)\n\n# \u5b8c\u6210\u540e\u91ca\u653e\ndroid.wakeLockRelease()\n\n# \u6216\u4f7f\u7528\u90e8\u5206\u9501\u8fdb\u884c\u540e\u53f0\u4efb\u52a1\ndroid.wakeLockAcquirePartial()\n# \u5373\u4f7f\u5c4f\u5e55\u5173\u95ed\uff0cCPU \u4ecd\u4fdd\u6301\u5f00\u542f\ntime.sleep(30)\ndroid.wakeLockRelease()\n

\u6ce8\u610f\uff1a \u8bb0\u4f4f\u5728\u4e0d\u9700\u8981\u65f6\u91ca\u653e\u5524\u9192\u9501\u4ee5\u8282\u7701\u7535\u6c60\u3002

"},{"location":"qsl4a/ui/accessibility/","title":"\u65e0\u969c\u788d\u670d\u52a1","text":"

\u65e0\u969c\u788d\u670d\u52a1\u5141\u8bb8\u81ea\u52a8\u5316 UI \u4ea4\u4e92\uff0c\u5982\u70b9\u51fb\u3001\u6ed1\u52a8\u548c\u7cfb\u7edf\u64cd\u4f5c\u3002

"},{"location":"qsl4a/ui/accessibility/#_2","title":"\u670d\u52a1\u63a7\u5236","text":""},{"location":"qsl4a/ui/accessibility/#accessibilitystartservice","title":"accessibilityStartService()","text":"

\u542f\u52a8\u65e0\u969c\u788d\u670d\u52a1\u3002

accessibilityStartService()\n

\u8fd4\u56de\uff1a \u5982\u679c\u6210\u529f\u5219\u4e3a True\uff0c\u5426\u5219\u4e3a False

"},{"location":"qsl4a/ui/accessibility/#accessibilityserviceenabled","title":"accessibilityServiceEnabled()","text":"

\u68c0\u67e5\u65e0\u969c\u788d\u670d\u52a1\u662f\u5426\u5df2\u542f\u7528\u3002

accessibilityServiceEnabled()\n

\u8fd4\u56de\uff1a True \u6216 False

"},{"location":"qsl4a/ui/accessibility/#_3","title":"\u5c4f\u5e55\u4ea4\u4e92","text":""},{"location":"qsl4a/ui/accessibility/#accessibilityclick","title":"accessibilityClick()","text":"

\u5728\u5c4f\u5e55\u5750\u6807\u5904\u70b9\u51fb\u3002

accessibilityClick(x=0, y=0, t=50)\n

\u53c2\u6570\uff1a - x (int/float): X \u5750\u6807\uff080=\u5c45\u4e2d\uff0c\u652f\u6301\u5c0f\u6570\uff09 - y (int/float): Y \u5750\u6807\uff080=\u5c45\u4e2d\uff0c\u652f\u6301\u5c0f\u6570\uff09 - t (int): \u6309\u4f4f\u65f6\u957f\uff08\u6beb\u79d2\uff09\uff08\u9ed8\u8ba4\uff1a50\uff09

"},{"location":"qsl4a/ui/accessibility/#accessibilityslide","title":"accessibilitySlide()","text":"

\u591a\u70b9\u6ed1\u52a8\u624b\u52bf\u3002

accessibilitySlide(XnYn=None, t=None)\n

\u53c2\u6570\uff1a - XnYn (list): \u5750\u6807 [X1, Y1, X2, Y2, ... Xn, Yn] - t (int): \u6ed1\u52a8\u65f6\u957f\uff08\u9ed8\u8ba4\uff1a50*n \u6beb\u79d2\uff09

"},{"location":"qsl4a/ui/accessibility/#_4","title":"\u7cfb\u7edf\u64cd\u4f5c","text":""},{"location":"qsl4a/ui/accessibility/#accessibilityaction","title":"accessibilityAction()","text":"

\u6309\u4ee3\u7801\u6267\u884c\u7cfb\u7edf\u64cd\u4f5c\u3002

accessibilityAction(actionCode)\n

\u64cd\u4f5c\u4ee3\u7801\uff1a | \u4ee3\u7801 | \u5e38\u91cf | \u63cf\u8ff0 | |------|----------|-------------| | 1 | BACK | \u8fd4\u56de\u6309\u94ae | | 2 | HOME | \u4e3b\u5c4f\u5e55\u6309\u94ae | | 3 | RECENTS | \u6700\u8fd1\u5e94\u7528 | | 4 | NOTIFICATIONS | \u6253\u5f00\u901a\u77e5 | | 5 | QUICK_SETTINGS | \u6253\u5f00\u5feb\u901f\u8bbe\u7f6e | | 6 | POWER_DIALOG | \u7535\u6e90\u83dc\u5355 | | 7 | TOGGLE_SPLIT_SCREEN | \u5206\u5c4f | | 8 | LOCK_SCREEN | \u9501\u5c4f | | 9 | TAKE_SCREENSHOT | \u622a\u56fe | | 10 | KEYCODE_HEADSETHOOK | \u8033\u673a\u6302\u94a9 | | 11-14 | ACCESSIBILITY_ | \u65e0\u969c\u788d\u6309\u94ae | | 15 | DISMISS_NOTIFICATION_SHADE | \u5173\u95ed\u901a\u77e5\u680f | | 16-20 | DPAD_ | \u65b9\u5411\u952e | | 21 | MENU | \u83dc\u5355\u6309\u94ae | | 22 | MEDIA_PLAY_PAUSE | \u64ad\u653e/\u6682\u505c\u5a92\u4f53 |

"},{"location":"qsl4a/ui/accessibility/#_5","title":"\u4f7f\u7528\u793a\u4f8b","text":""},{"location":"qsl4a/ui/accessibility/#_6","title":"\u70b9\u51fb\u5c4f\u5e55\u4e2d\u5fc3","text":"
import androidhelper\n\ndroid = androidhelper.Android()\n\n# \u542f\u52a8\u670d\u52a1\nif not droid.accessibilityServiceEnabled().result:\n    droid.accessibilityStartService()\n\n# \u70b9\u51fb\u4e2d\u5fc3\uff080,0 = \u4e2d\u5fc3\uff09\ndroid.accessibilityClick(0, 0, t=100)\n
"},{"location":"qsl4a/ui/accessibility/#_7","title":"\u6ed1\u52a8\u624b\u52bf","text":"
# \u4ece\u4e0b\u5f80\u4e0a\u6ed1\u52a8\uff08\u5411\u4e0a\u6eda\u52a8\uff09\ndroid.accessibilitySlide([0, 1.5, 0, -1.5], t=300)\n
"},{"location":"qsl4a/ui/accessibility/#_8","title":"\u7cfb\u7edf\u5bfc\u822a","text":"
# \u6309\u4e3b\u5c4f\u5e55\ndroid.accessibilityAction(2)\n\n# \u6309\u8fd4\u56de\ndroid.accessibilityAction(1)\n\n# \u6253\u5f00\u901a\u77e5\ndroid.accessibilityAction(4)\n
"},{"location":"qsl4a/ui/accessibility/#_9","title":"\u70b9\u51fb\u7279\u5b9a\u4f4d\u7f6e","text":"
# \u70b9\u51fb\u5c4f\u5e55\u5750\u6807 (500, 800)\ndroid.accessibilityClick(500, 800)\n\n# \u70b9\u51fb\u76f8\u5bf9\u4f4d\u7f6e\uff08\u6c34\u5e73\u5c45\u4e2d\uff0c\u5782\u76f4 3/4 \u5904\uff09\ndroid.accessibilityClick(0, 1.5)\n
"},{"location":"qsl4a/ui/dialogs/","title":"\u5bf9\u8bdd\u6846\u7cfb\u7edf","text":"

QSL4A \u63d0\u4f9b\u5168\u9762\u7684\u5bf9\u8bdd\u6846\u652f\u6301\uff0c\u7528\u4e8e\u7528\u6237\u4ea4\u4e92\uff0c\u5305\u62ec\u8b66\u62a5\u3001\u8f93\u5165\u5bf9\u8bdd\u6846\u3001\u9009\u62e9\u5bf9\u8bdd\u6846\u548c\u8fdb\u5ea6\u5bf9\u8bdd\u6846\u3002

"},{"location":"qsl4a/ui/dialogs/#_2","title":"\u8b66\u62a5\u5bf9\u8bdd\u6846","text":""},{"location":"qsl4a/ui/dialogs/#dialogshowalert","title":"dialogShowAlert()","text":"

\u663e\u793a\u7b80\u5355\u8b66\u62a5\u5bf9\u8bdd\u6846\u3002

dialogShowAlert(title=\"Alert\", message=\"The message of the alert.\",\n                positiveButtonText=\"OK\", negativeButtonText=None,\n                neutralButtonText=None, messageIsHtml=False)\n

\u53c2\u6570\uff1a - title (str): \u5bf9\u8bdd\u6846\u6807\u9898 - message (str): \u6d88\u606f\u6587\u672c - positiveButtonText (str): \u80af\u5b9a\u6309\u94ae\u6807\u7b7e - negativeButtonText (str, optional): \u5426\u5b9a\u6309\u94ae\u6807\u7b7e - neutralButtonText (str, optional): \u4e2d\u6027\u6309\u94ae\u6807\u7b7e - messageIsHtml (bool): \u5c06\u6d88\u606f\u89e3\u6790\u4e3a HTML

\u8fd4\u56de\uff1a \u5305\u542b\u70b9\u51fb\u6309\u94ae\u7684\u7ed3\u679c

"},{"location":"qsl4a/ui/dialogs/#dialogshowsimplechoice","title":"dialogShowSimpleChoice()","text":"

\u663e\u793a\u5e26\u6709\u9879\u76ee\u7684\u7b80\u5355\u9009\u62e9\u5bf9\u8bdd\u6846\u3002

dialogShowSimpleChoice(title=\"Alert\", message=\"The message of the alert.\",\n                       items=None, positiveButtonText=\"OK\",\n                       negativeButtonText=None, neutralButtonText=None,\n                       messageIsHtml=False)\n

\u53c2\u6570\uff1a - items (list): \u9009\u62e9\u5b57\u7b26\u4e32\u5217\u8868

\u8fd4\u56de\uff1a \u5305\u542b\u9009\u4e2d\u9879\u76ee\u7684\u7ed3\u679c

"},{"location":"qsl4a/ui/dialogs/#_3","title":"\u8f93\u5165\u5bf9\u8bdd\u6846","text":""},{"location":"qsl4a/ui/dialogs/#dialoggetinput","title":"dialogGetInput()","text":"

\u83b7\u53d6\u7528\u6237\u6587\u672c\u8f93\u5165\u3002

dialogGetInput(title=\"Value\", message=\"Please enter value:\",\n               defaultText=None, messageIsHtml=False)\n

\u8fd4\u56de\uff1a \u5305\u542b\u7528\u6237\u8f93\u5165\u6587\u672c\u7684\u7ed3\u679c

"},{"location":"qsl4a/ui/dialogs/#dialoggetpassword","title":"dialogGetPassword()","text":"

\u83b7\u53d6\u5bc6\u7801\u8f93\u5165\u3002

dialogGetPassword(title=\"Password\", message=\"Please enter password:\")\n

\u8fd4\u56de\uff1a \u5305\u542b\u8f93\u5165\u5bc6\u7801\u7684\u7ed3\u679c

"},{"location":"qsl4a/ui/dialogs/#dialogcreateinput","title":"dialogCreateInput()","text":"

\u521b\u5efa\u81ea\u5b9a\u4e49\u8f93\u5165\u5bf9\u8bdd\u6846\u3002

dialogCreateInput(title=\"Value\", message=\"Please enter value:\",\n                  defaultText=None, inputType=None)\n

\u53c2\u6570\uff1a - inputType (str): \u8f93\u5165\u7c7b\u578b\uff08\u4f8b\u5982 \"text\"\u3001\"number\"\u3001\"textPassword\"\uff09

"},{"location":"qsl4a/ui/dialogs/#dialogcreatepassword","title":"dialogCreatePassword()","text":"

\u521b\u5efa\u5bc6\u7801\u8f93\u5165\u5bf9\u8bdd\u6846\u3002

dialogCreatePassword(title=\"Password\", message=\"Please enter password:\")\n
"},{"location":"qsl4a/ui/dialogs/#dialogcreateseekbar","title":"dialogCreateSeekBar()","text":"

\u521b\u5efa\u6ed1\u5757/\u8fdb\u5ea6\u6761\u5bf9\u8bdd\u6846\u3002

dialogCreateSeekBar(starting_value=50, maximum_value=100, title=\"\", message=\"\")\n

\u53c2\u6570\uff1a - starting_value (int): \u521d\u59cb\u503c - maximum_value (int): \u6700\u5927\u503c

"},{"location":"qsl4a/ui/dialogs/#_4","title":"\u9009\u62e9\u5bf9\u8bdd\u6846","text":""},{"location":"qsl4a/ui/dialogs/#dialogshowsinglechoice","title":"dialogShowSingleChoice()","text":"

\u663e\u793a\u5355\u9009\uff08\u5355\u9009\u6309\u94ae\uff09\u5bf9\u8bdd\u6846\u3002

dialogShowSingleChoice(title=\"Alert\", message=\"The message of the alert.\",\n                       items=None, selected=-1, positiveButtonText=\"OK\",\n                       negativeButtonText=None, neutralButtonText=None,\n                       messageIsHtml=False)\n

\u53c2\u6570\uff1a - items (list): \u9009\u62e9\u5b57\u7b26\u4e32\u5217\u8868 - selected (int): \u9ed8\u8ba4\u9009\u4e2d\u7d22\u5f15

"},{"location":"qsl4a/ui/dialogs/#dialogshowmultichoice","title":"dialogShowMultiChoice()","text":"

\u663e\u793a\u591a\u9009\uff08\u590d\u9009\u6846\uff09\u5bf9\u8bdd\u6846\u3002

dialogShowMultiChoice(title=\"Alert\", message=\"The message of the alert.\",\n                      items=None, selected=None, positiveButtonText=\"OK\",\n                      negativeButtonText=None, neutralButtonText=None,\n                      messageIsHtml=False)\n

\u53c2\u6570\uff1a - items (list): \u9009\u62e9\u5b57\u7b26\u4e32\u5217\u8868 - selected (list): \u521d\u59cb\u9009\u4e2d\u7d22\u5f15\u5217\u8868

"},{"location":"qsl4a/ui/dialogs/#dialogsetsinglechoiceitems","title":"dialogSetSingleChoiceItems()","text":"

\u4e3a\u5bf9\u8bdd\u6846\u8bbe\u7f6e\u5355\u9009\u9879\u76ee\u3002

dialogSetSingleChoiceItems(items, selected=-1)\n
"},{"location":"qsl4a/ui/dialogs/#dialogsetmultichoiceitems","title":"dialogSetMultiChoiceItems()","text":"

\u4e3a\u5bf9\u8bdd\u6846\u8bbe\u7f6e\u591a\u9009\u9879\u76ee\u3002

dialogSetMultiChoiceItems(items, selected=None)\n
"},{"location":"qsl4a/ui/dialogs/#_5","title":"\u8fdb\u5ea6\u5bf9\u8bdd\u6846","text":""},{"location":"qsl4a/ui/dialogs/#dialogcreatespinnerprogress","title":"dialogCreateSpinnerProgress()","text":"

\u521b\u5efa\u4e0d\u786e\u5b9a\u8fdb\u5ea6\u5bf9\u8bdd\u6846\u3002

dialogCreateSpinnerProgress(title=None, message=None, maximum_progress=100)\n
"},{"location":"qsl4a/ui/dialogs/#dialogcreatehorizontalprogress","title":"dialogCreateHorizontalProgress()","text":"

\u521b\u5efa\u6c34\u5e73\u8fdb\u5ea6\u5bf9\u8bdd\u6846\u3002

dialogCreateHorizontalProgress(title=None, message=None, maximum_progress=100)\n
"},{"location":"qsl4a/ui/dialogs/#dialogsetcurrentprogress","title":"dialogSetCurrentProgress()","text":"

\u66f4\u65b0\u8fdb\u5ea6\u503c\u3002

dialogSetCurrentProgress(current)\n
"},{"location":"qsl4a/ui/dialogs/#dialogsetmaxprogress","title":"dialogSetMaxProgress()","text":"

\u8bbe\u7f6e\u6700\u5927\u8fdb\u5ea6\u503c\u3002

dialogSetMaxProgress(max)\n
"},{"location":"qsl4a/ui/dialogs/#dialogsetprogressmessage","title":"dialogSetProgressMessage()","text":"

\u66f4\u65b0\u8fdb\u5ea6\u5bf9\u8bdd\u6846\u6d88\u606f\u3002

dialogSetProgressMessage(message)\n
"},{"location":"qsl4a/ui/dialogs/#_6","title":"\u9009\u62e9\u5668\u5bf9\u8bdd\u6846","text":""},{"location":"qsl4a/ui/dialogs/#dialogcreatedatepicker","title":"dialogCreateDatePicker()","text":"

\u521b\u5efa\u65e5\u671f\u9009\u62e9\u5668\u5bf9\u8bdd\u6846\u3002

dialogCreateDatePicker(year=1970, month=1, day=1)\n
"},{"location":"qsl4a/ui/dialogs/#dialogcreatetimepicker","title":"dialogCreateTimePicker()","text":"

\u521b\u5efa\u65f6\u95f4\u9009\u62e9\u5668\u5bf9\u8bdd\u6846\u3002

dialogCreateTimePicker(hour=0, minute=0, is24hour=False)\n
"},{"location":"qsl4a/ui/dialogs/#_7","title":"\u81ea\u5b9a\u4e49\u5bf9\u8bdd\u6846\u521b\u5efa","text":""},{"location":"qsl4a/ui/dialogs/#dialogcreatealert","title":"dialogCreateAlert()","text":"

\u521b\u5efa\u81ea\u5b9a\u4e49\u8b66\u62a5\u5bf9\u8bdd\u6846\u3002

dialogCreateAlert(title=None, message=None)\n

\u521b\u5efa\u4e00\u4e2a\u7a7a\u8b66\u62a5\u5bf9\u8bdd\u6846\u3002\u53ef\u4e0e\u5176\u4ed6 dialogSet* \u51fd\u6570\u4e00\u8d77\u4f7f\u7528\u6765\u81ea\u5b9a\u4e49\u3002

"},{"location":"qsl4a/ui/dialogs/#dialogsetitems","title":"dialogSetItems()","text":"

\u4e3a\u5bf9\u8bdd\u6846\u8bbe\u7f6e\u7b80\u5355\u5217\u8868\u9879\u76ee\u3002

dialogSetItems(items)\n
"},{"location":"qsl4a/ui/dialogs/#dialogsetpositivebuttontext","title":"dialogSetPositiveButtonText()","text":"

\u8bbe\u7f6e\u80af\u5b9a\u6309\u94ae\u6587\u672c\u3002

dialogSetPositiveButtonText(text)\n
"},{"location":"qsl4a/ui/dialogs/#dialogsetnegativebuttontext","title":"dialogSetNegativeButtonText()","text":"

\u8bbe\u7f6e\u5426\u5b9a\u6309\u94ae\u6587\u672c\u3002

dialogSetNegativeButtonText(text)\n
"},{"location":"qsl4a/ui/dialogs/#dialogsetneutralbuttontext","title":"dialogSetNeutralButtonText()","text":"

\u8bbe\u7f6e\u4e2d\u6027\u6309\u94ae\u6587\u672c\u3002

dialogSetNeutralButtonText(text)\n
"},{"location":"qsl4a/ui/dialogs/#dialogsetmessageishtml","title":"dialogSetMessageIsHtml()","text":"

\u8bbe\u7f6e\u6d88\u606f\u662f\u5426\u5e94\u89e3\u6790\u4e3a HTML\u3002

dialogSetMessageIsHtml(messageIsHtml=True)\n
"},{"location":"qsl4a/ui/dialogs/#dialogshow","title":"dialogShow()","text":"

\u663e\u793a\u521b\u5efa\u7684\u81ea\u5b9a\u4e49\u5bf9\u8bdd\u6846\u3002

dialogShow()\n
"},{"location":"qsl4a/ui/dialogs/#_8","title":"\u5bf9\u8bdd\u6846\u63a7\u5236","text":""},{"location":"qsl4a/ui/dialogs/#dialogdismiss","title":"dialogDismiss()","text":"

\u5173\u95ed\u5f53\u524d\u5bf9\u8bdd\u6846\u3002

dialogDismiss()\n
"},{"location":"qsl4a/ui/dialogs/#dialoggetresponse","title":"dialogGetResponse()","text":"

\u83b7\u53d6\u5bf9\u8bdd\u6846\u54cd\u5e94\u3002

dialogGetResponse()\n
"},{"location":"qsl4a/ui/dialogs/#dialoggetselecteditems","title":"dialogGetSelectedItems()","text":"

\u4ece\u9009\u62e9\u5bf9\u8bdd\u6846\u83b7\u53d6\u9009\u4e2d\u7684\u9879\u76ee\u3002

dialogGetSelectedItems()\n
"},{"location":"qsl4a/ui/dialogs/#_9","title":"\u4f7f\u7528\u793a\u4f8b","text":""},{"location":"qsl4a/ui/dialogs/#_10","title":"\u7b80\u5355\u8b66\u62a5","text":"
import androidhelper\n\ndroid = androidhelper.Android()\n\n# \u663e\u793a\u8b66\u62a5\ndroid.dialogShowAlert(\"Warning\", \"This is an important message!\")\n
"},{"location":"qsl4a/ui/dialogs/#_11","title":"\u8f93\u5165\u5bf9\u8bdd\u6846","text":"
# \u83b7\u53d6\u7528\u6237\u8f93\u5165\nresult = droid.dialogGetInput(\"Name\", \"Enter your name:\", \"John\").result\nprint(f\"Hello, {result}!\")\n
"},{"location":"qsl4a/ui/dialogs/#_12","title":"\u5e26\u6309\u94ae\u7684\u81ea\u5b9a\u4e49\u5bf9\u8bdd\u6846","text":"
# \u521b\u5efa\u81ea\u5b9a\u4e49\u5bf9\u8bdd\u6846\ndroid.dialogCreateAlert(\"Custom\", \"Choose an option\")\ndroid.dialogSetItems([\"Option 1\", \"Option 2\", \"Option 3\"])\ndroid.dialogShow()\n\n# \u83b7\u53d6\u54cd\u5e94\nresponse = droid.dialogGetResponse().result\nprint(f\"Selected: {response['item']}\")\n
"},{"location":"qsl4a/ui/dialogs/#_13","title":"\u8fdb\u5ea6\u5bf9\u8bdd\u6846","text":"
# \u521b\u5efa\u8fdb\u5ea6\u5bf9\u8bdd\u6846\ndroid.dialogCreateHorizontalProgress(\"Loading\", \"Please wait...\", 100)\ndroid.dialogShow()\n\n# \u66f4\u65b0\u8fdb\u5ea6\nfor i in range(101):\n    droid.dialogSetCurrentProgress(i)\n    time.sleep(0.05)\n\ndroid.dialogDismiss()\n
"},{"location":"qsl4a/ui/dialogs/#_14","title":"\u65e5\u671f\u9009\u62e9\u5668","text":"
# \u663e\u793a\u65e5\u671f\u9009\u62e9\u5668\ndroid.dialogCreateDatePicker(2024, 1, 15)\ndroid.dialogShow()\nresponse = droid.dialogGetResponse().result\nprint(f\"Selected: {response}\")\n
"},{"location":"qsl4a/ui/floatview/","title":"\u6d6e\u52a8\u89c6\u56fe","text":"

\u6d6e\u52a8\u7a97\u53e3\u652f\u6301\uff0c\u7528\u4e8e\u5728\u5176\u4ed6\u5e94\u7528\u7a0b\u5e8f\u4e0a\u65b9\u4fdd\u6301\u663e\u793a\u7684\u8986\u76d6 UI \u5143\u7d20\u3002

"},{"location":"qsl4a/ui/floatview/#_2","title":"\u65b9\u6cd5","text":""},{"location":"qsl4a/ui/floatview/#floatview","title":"floatView()","text":"

\u663e\u793a\u6216\u4fee\u6539\u6d6e\u52a8\u89c6\u56fe\u3002

floatView(Args=None)\n

\u53c2\u6570\uff1a - Args (dict): \u914d\u7f6e\u5b57\u5178\uff0c\u5305\u542b\u4ee5\u4e0b\u952e\uff1a - index (int): \u6d6e\u52a8\u89c6\u56fe\u7d22\u5f15\uff08-1 \u521b\u5efa\u65b0\u7684\uff0c>=0 \u4fee\u6539\u73b0\u6709\u7684\uff09 - text (str): \u8981\u663e\u793a\u7684\u6587\u672c\u5185\u5bb9 - html (str): HTML \u5185\u5bb9\uff08\u5982\u679c\u7701\u7565 text \u5219\u4f7f\u7528\uff09 - width (int): \u89c6\u56fe\u5bbd\u5ea6\uff08\u50cf\u7d20\uff09\uff08\u9ed8\u8ba4\uff1a300\uff09 - height (int): \u89c6\u56fe\u9ad8\u5ea6\uff08\u50cf\u7d20\uff09\uff08\u9ed8\u8ba4\uff1a150\uff09 - x (int): X \u4f4d\u7f6e\uff080 = \u5c45\u4e2d\uff0c\u6b63/\u8d1f\u8868\u793a\u504f\u79fb\uff09 - y (int): Y \u4f4d\u7f6e\uff080 = \u5c45\u4e2d\uff0c\u6b63/\u8d1f\u8868\u793a\u504f\u79fb\uff09 - backColor (str): ARGB \u5341\u516d\u8fdb\u5236\u80cc\u666f\u8272\uff08\u9ed8\u8ba4\uff1a'7f7f7f7f'\uff09 - textColor (str): ARGB \u5341\u516d\u8fdb\u5236\u6587\u672c\u8272\uff08\u9ed8\u8ba4\uff1a'ff000000'\uff09 - textSize (int): \u6587\u672c\u5927\u5c0f\uff08\u9ed8\u8ba4\uff1a10\uff09 - textAlign (int): \u6587\u672c\u5bf9\u9f50\uff080 = \u7ee7\u627f\uff09 - script (str): \u957f\u6309\u5173\u95ed\u540e\u8fd0\u884c\u7684\u811a\u672c\u8def\u5f84 - arg: \u811a\u672c\u53c2\u6570 - clickRemove (bool): \u542f\u7528\u70b9\u51fb\u79fb\u9664\uff08\u9ed8\u8ba4\uff1aTrue\uff09 - flag (int): \u7a97\u53e3\u6807\u5fd7\uff08\u9ed8\u8ba4\uff1a40 - \u53ef\u89e6\u6478\uff09

\u8fd4\u56de\uff1a \u5f53\u524d\u94fe\u5217\u8868\u957f\u5ea6

"},{"location":"qsl4a/ui/floatview/#floatviewcount","title":"floatViewCount()","text":"

\u83b7\u53d6\u6d3b\u52a8\u6d6e\u52a8\u89c6\u56fe\u7684\u6570\u91cf\u3002

floatViewCount()\n

\u8fd4\u56de\uff1a \u6d6e\u52a8\u89c6\u56fe\u6570\u91cf

"},{"location":"qsl4a/ui/floatview/#floatviewresult","title":"floatViewResult()","text":"

\u83b7\u53d6\u6d6e\u52a8\u89c6\u56fe\u7684\u7ed3\u679c/\u72b6\u6001\u3002

floatViewResult(index=-1)\n

\u53c2\u6570\uff1a - index (int): \u6d6e\u52a8\u89c6\u56fe\u7d22\u5f15\uff08\u9ed8\u8ba4\uff1a-1\uff0c\u8fd4\u56de\u6700\u540e\u64cd\u4f5c\u7ed3\u679c\uff09

\u8fd4\u56de\uff1a \u5305\u542b\u64cd\u4f5c\u8be6\u60c5\u7684\u5b57\u5178\uff0c\u5305\u62ec\uff1a - x, y: \u4f4d\u7f6e - time: \u65f6\u95f4\u6233 - operation: \u64cd\u4f5c\u7c7b\u578b\uff08'initial'\u3001'move' \u7b49\uff09 - index: \u89c6\u56fe\u7d22\u5f15 - removed: \u5982\u679c\u89c6\u56fe\u88ab\u79fb\u9664\u5219\u4e3a True

"},{"location":"qsl4a/ui/floatview/#floatviewremove","title":"floatViewRemove()","text":"

\u79fb\u9664\u6d6e\u52a8\u89c6\u56fe\u3002

floatViewRemove(index=-1)\n

\u53c2\u6570\uff1a - index (int): \u8981\u79fb\u9664\u7684\u89c6\u56fe\u7d22\u5f15\uff08\u9ed8\u8ba4\uff1a-1 \u79fb\u9664\u6700\u540e\u4e00\u4e2a\uff09

\u8fd4\u56de\uff1a \u5982\u679c\u6210\u529f\u5219\u4e3a 1\uff0c\u5426\u5219\u4e3a 0

"},{"location":"qsl4a/ui/floatview/#_3","title":"\u5e38\u91cf","text":"
  • floatView.INDEX_NEW = -1 - \u521b\u5efa\u65b0\u7684\u6d6e\u52a8\u89c6\u56fe
  • floatView.FLAG_DEFAULT_TOUCHABLE = 40 - \u9ed8\u8ba4\u53ef\u89e6\u6478\u6807\u5fd7
  • floatView.TEXT_ALIGNMENT_INHERIT = 0
  • floatView.TEXT_ALIGNMENT_CENTER - \u5c45\u4e2d\u6587\u672c\u5bf9\u9f50
"},{"location":"qsl4a/ui/floatview/#_4","title":"\u4f7f\u7528\u793a\u4f8b","text":""},{"location":"qsl4a/ui/floatview/#_5","title":"\u57fa\u672c\u6d6e\u52a8\u89c6\u56fe","text":"
import androidhelper\nfrom androidhelper import Android\n\ndroid = androidhelper.Android()\n\n# \u521b\u5efa\u7b80\u5355\u6d6e\u52a8\u89c6\u56fe\ndroid.floatView({\n    'index': -1,  # \u521b\u5efa\u65b0\u7684\n    'text': 'Hello World',\n    'width': 400,\n    'height': 300,\n    'x': -300,  # \u4ece\u4e2d\u5fc3\u504f\u79fb\n    'y': -400,\n    'backColor': 'ff0000',  # \u7ea2\u8272\u80cc\u666f\n    'textColor': '0000ff',  # \u84dd\u8272\u6587\u672c\n    'textSize': 16,\n    'textAlign': droid.floatView.TEXT_ALIGNMENT_CENTER\n})\n\n# \u68c0\u67e5\u6570\u91cf\nprint(f\"Float views: {droid.floatViewCount().result}\")\n\n# \u83b7\u53d6\u7ed3\u679c\nresult = droid.floatViewResult().result\nprint(f\"View info: {result}\")\n\n# \u79fb\u9664\u6d6e\u52a8\u89c6\u56fe\ndroid.floatViewRemove(0)\n
"},{"location":"qsl4a/ui/floatview/#html","title":"HTML \u5185\u5bb9","text":"
# \u521b\u5efa\u5e26 HTML \u5185\u5bb9\u7684\u6d6e\u52a8\u89c6\u56fe\ndroid.floatView({\n    'text': '',  # \u7a7a\u6587\u672c\u4ee5\u4f7f\u7528 HTML\n    'html': '<h1>Title</h1><p>Rich <b>HTML</b> content</p>',\n    'width': 500,\n    'height': 400\n})\n
"},{"location":"qsl4a/ui/floatview/#_6","title":"\u4fee\u6539\u73b0\u6709\u89c6\u56fe","text":"
# \u521b\u5efa\u521d\u59cb\u89c6\u56fe\ndroid.floatView({'text': 'Initial Text', 'width': 300, 'height': 150})\n\n# \u4fee\u6539\u540c\u4e00\u89c6\u56fe\uff08\u7d22\u5f15 0\uff09\ndroid.floatView({\n    'index': 0,\n    'text': 'Updated Text!',\n    'backColor': '7f00ff00'  # \u7eff\u8272\u80cc\u666f\n})\n\n# \u68c0\u67e5\u79fb\u52a8/\u66f4\u6539\u7ed3\u679c\nresult = droid.floatViewResult(0).result\nprint(f\"Operation: {result.get('operation')}, Position: ({result.get('x')}, {result.get('y')})\")\n
"},{"location":"qsl4a/ui/floatview/#_7","title":"\u591a\u4e2a\u6d6e\u52a8\u89c6\u56fe","text":"
# \u521b\u5efa\u591a\u4e2a\u89c6\u56fe\nfor i in range(3):\n    droid.floatView({\n        'index': -1,\n        'text': f'View {i}',\n        'x': i * 100 - 150,\n        'y': i * 100 - 150,\n        'backColor': f'{i}f{i}f{i}f'\n    })\n\nprint(f\"Total views: {droid.floatViewCount().result}\")\n\n# \u79fb\u9664\u6240\u6709\u89c6\u56fe\nwhile droid.floatViewCount().result > 0:\n    droid.floatViewRemove()\n
"},{"location":"qsl4a/ui/floatview/#_8","title":"\u5e26\u811a\u672c\u56de\u8c03","text":"
# \u521b\u5efa\u5173\u95ed\u65f6\u8fd0\u884c\u811a\u672c\u7684\u6d6e\u52a8\u89c6\u56fe\ndroid.floatView({\n    'text': 'Click to close and run script',\n    'script': '/sdcard/my_script.py',\n    'arg': 'hello from float view',\n    'clickRemove': True\n})\n
"},{"location":"qsl4a/ui/fullscreen/","title":"\u5168\u5c4f UI","text":"

\u4f7f\u7528\u539f\u751f Android \u5e03\u5c40\u521b\u5efa\u81ea\u5b9a\u4e49\u5168\u5c4f\u754c\u9762\u3002

"},{"location":"qsl4a/ui/fullscreen/#_1","title":"\u5e03\u5c40\u65b9\u6cd5","text":""},{"location":"qsl4a/ui/fullscreen/#fullshow","title":"fullShow()","text":"

\u663e\u793a\u5168\u5c4f\u5e03\u5c40\u3002

fullShow(layout, title=None, theme=None)\n

\u53c2\u6570\uff1a - layout (str): JSON \u5e03\u5c40\u5b9a\u4e49\u6216\u5e03\u5c40\u5b57\u7b26\u4e32 - title (str, optional): \u7a97\u53e3\u6807\u9898 - theme (str, optional): \u4e3b\u9898\u540d\u79f0

\u8fd4\u56de\uff1a \u7a97\u53e3 ID

"},{"location":"qsl4a/ui/fullscreen/#fulldismiss","title":"fullDismiss()","text":"

\u5173\u95ed\u5168\u5c4f\u7a97\u53e3\u3002

fullDismiss()\n
"},{"location":"qsl4a/ui/fullscreen/#fullquery","title":"fullQuery()","text":"

\u67e5\u8be2\u6240\u6709\u5c0f\u90e8\u4ef6\u503c\u3002

fullQuery()\n

\u8fd4\u56de\uff1a \u5c0f\u90e8\u4ef6 ID \u548c\u503c\u7684\u5b57\u5178

"},{"location":"qsl4a/ui/fullscreen/#fullquerydetail","title":"fullQueryDetail()","text":"

\u67e5\u8be2\u7279\u5b9a\u5c0f\u90e8\u4ef6\u8be6\u60c5\u3002

fullQueryDetail(id)\n
"},{"location":"qsl4a/ui/fullscreen/#_2","title":"\u5c5e\u6027\u65b9\u6cd5","text":""},{"location":"qsl4a/ui/fullscreen/#fullgetproperty","title":"fullGetProperty()","text":"

\u83b7\u53d6\u5c0f\u90e8\u4ef6\u5c5e\u6027\u3002

fullGetProperty(id, property)\n
"},{"location":"qsl4a/ui/fullscreen/#fullsetproperty","title":"fullSetProperty()","text":"

\u8bbe\u7f6e\u5c0f\u90e8\u4ef6\u5c5e\u6027\u3002

fullSetProperty(id, property, value)\n
"},{"location":"qsl4a/ui/fullscreen/#fullsetlist","title":"fullSetList()","text":"

\u8bbe\u7f6e\u5217\u8868\u5c0f\u90e8\u4ef6\u9879\u76ee\u3002

fullSetList(id, list, isHtml=False, listType=0)\n
"},{"location":"qsl4a/ui/fullscreen/#fullsetlist2","title":"fullSetList2()","text":"

\u4f7f\u7528\u8d44\u6e90 ID \u8bbe\u7f6e\u5217\u8868\u5c0f\u90e8\u4ef6\u9879\u76ee\u3002

fullSetList2(id, list, intRes)\n

\u53c2\u6570\uff1a - id (str): \u5c0f\u90e8\u4ef6 ID - list (list): \u9879\u76ee\u5217\u8868 - intRes (int): \u5217\u8868\u9879\u76ee\u5e03\u5c40\u7684 Android \u8d44\u6e90 ID

"},{"location":"qsl4a/ui/fullscreen/#fullsetlistselected","title":"fullSetListSelected()","text":"

\u5728\u5217\u8868\u4e2d\u8bbe\u7f6e\u9009\u4e2d\u9879\u76ee\u3002

fullSetListSelected(id, selected)\n

\u53c2\u6570\uff1a - id (str): \u5217\u8868\u5c0f\u90e8\u4ef6 ID - selected (int): \u8981\u9009\u4e2d\u7684\u9879\u76ee\u7d22\u5f15

"},{"location":"qsl4a/ui/fullscreen/#fullgetlistselected","title":"fullGetListSelected()","text":"

\u83b7\u53d6\u5f53\u524d\u9009\u4e2d\u7684\u5217\u8868\u9879\u76ee\u7d22\u5f15\u3002

fullGetListSelected(id)\n

\u53c2\u6570\uff1a - id (str): \u5217\u8868\u5c0f\u90e8\u4ef6 ID

\u8fd4\u56de\uff1a \u9009\u4e2d\u9879\u76ee\u7d22\u5f15

"},{"location":"qsl4a/ui/fullscreen/#_3","title":"\u6279\u91cf\u5c5e\u6027\u64cd\u4f5c","text":""},{"location":"qsl4a/ui/fullscreen/#fullgetproperties","title":"fullGetProperties()","text":"

\u4e00\u6b21\u83b7\u53d6\u591a\u4e2a\u5c0f\u90e8\u4ef6\u7684\u5c5e\u6027\u3002

fullGetProperties(ids, property)\n

\u53c2\u6570\uff1a - ids (list): \u5c0f\u90e8\u4ef6 ID \u5217\u8868 - property (str): \u8981\u83b7\u53d6\u7684\u5c5e\u6027\u540d

\u8fd4\u56de\uff1a \u6620\u5c04\u5c0f\u90e8\u4ef6 ID \u5230\u5c5e\u6027\u503c\u7684\u5b57\u5178

"},{"location":"qsl4a/ui/fullscreen/#fullsetproperties","title":"fullSetProperties()","text":"

\u4e00\u6b21\u8bbe\u7f6e\u591a\u4e2a\u5c0f\u90e8\u4ef6\u7684\u5c5e\u6027\u3002

fullSetProperties(ids, property, value)\n

\u53c2\u6570\uff1a - ids (list): \u5c0f\u90e8\u4ef6 ID \u5217\u8868 - property (str): \u8981\u8bbe\u7f6e\u7684\u5c5e\u6027\u540d - value: \u5c5e\u6027\u503c

"},{"location":"qsl4a/ui/fullscreen/#_4","title":"\u622a\u56fe","text":""},{"location":"qsl4a/ui/fullscreen/#fullgetscreenshot","title":"fullGetScreenShot()","text":"

\u6355\u83b7\u5168\u5c4f\u622a\u56fe\u3002

fullGetScreenShot(path=None)\n

\u53c2\u6570\uff1a - path (str, optional): \u4fdd\u5b58\u8def\u5f84\u3002\u5982\u679c\u4e3a None\uff0c\u5219\u5728\u4e8b\u4ef6\u4e2d\u8fd4\u56de

\u8fd4\u56de\uff1a \u5305\u542b\u622a\u56fe\u6570\u636e\u7684\u4e8b\u4ef6

"},{"location":"qsl4a/ui/fullscreen/#_5","title":"\u4f7f\u7528\u793a\u4f8b","text":"
import androidhelper\n\ndroid = androidhelper.Android()\n\n# \u5b9a\u4e49\u5e03\u5c40\nlayout = '''\n{ \"type\": \"LinearLayout\", \"orientation\": \"vertical\",\n  \"children\": [\n    { \"type\": \"TextView\", \"id\": \"title\", \"text\": \"Hello\" },\n    { \"type\": \"Button\", \"id\": \"btn\", \"text\": \"Click Me\" }\n  ]\n}\n'''\n\n# \u663e\u793a\u5e03\u5c40\ndroid.fullShow(layout, \"My App\")\n\n# \u67e5\u8be2\u6309\u94ae\u70b9\u51fb\nevent = droid.eventWaitFor('click', timeout=10)\nif event.result:\n    widget_id = event.result['data']['id']\n    if widget_id == 'btn':\n        print(\"Button clicked!\")\n\ndroid.fullDismiss()\n
"}]} \ No newline at end of file diff --git a/zh/sitemap.xml b/zh/sitemap.xml new file mode 100644 index 0000000..34bbcfb --- /dev/null +++ b/zh/sitemap.xml @@ -0,0 +1,203 @@ + + + + https://www.qpython.org/zh/ + 2026-04-21 + + + https://www.qpython.org/zh/AIPyApp/ + 2026-04-21 + + + https://www.qpython.org/zh/GraphicalInterface/ + 2026-04-21 + + + https://www.qpython.org/zh/Notebook/ + 2026-04-21 + + + https://www.qpython.org/zh/Ollama/ + 2026-04-21 + + + https://www.qpython.org/zh/Terminal/ + 2026-04-21 + + + https://www.qpython.org/zh/community/ + 2026-04-21 + + + https://www.qpython.org/zh/editor-guide/ + 2026-04-21 + + + https://www.qpython.org/zh/external-api/ + 2026-04-21 + + + https://www.qpython.org/zh/featured-courses/ + 2026-04-21 + + + https://www.qpython.org/zh/getting-started/ + 2026-04-21 + + + https://www.qpython.org/zh/qpypi-guide/ + 2026-04-21 + + + https://www.qpython.org/zh/qpython-x/ + 2026-04-21 + + + https://www.qpython.org/zh/tutorial-hello-world/ + 2026-04-21 + + + https://www.qpython.org/zh/whats-new/ + 2026-04-21 + + + https://www.qpython.org/zh/qsl4a/ + 2026-04-21 + + + https://www.qpython.org/zh/qsl4a/connectivity/contacts/ + 2026-04-21 + + + https://www.qpython.org/zh/qsl4a/connectivity/ftp/ + 2026-04-21 + + + https://www.qpython.org/zh/qsl4a/connectivity/location/ + 2026-04-21 + + + https://www.qpython.org/zh/qsl4a/connectivity/phone/ + 2026-04-21 + + + https://www.qpython.org/zh/qsl4a/connectivity/signalstrength/ + 2026-04-21 + + + https://www.qpython.org/zh/qsl4a/connectivity/sms/ + 2026-04-21 + + + https://www.qpython.org/zh/qsl4a/connectivity/wifi/ + 2026-04-21 + + + https://www.qpython.org/zh/qsl4a/core/android-base/ + 2026-04-21 + + + https://www.qpython.org/zh/qsl4a/core/events/ + 2026-04-21 + + + https://www.qpython.org/zh/qsl4a/core/intent/ + 2026-04-21 + + + https://www.qpython.org/zh/qsl4a/hardware/bluetooth/ + 2026-04-21 + + + https://www.qpython.org/zh/qsl4a/hardware/camera/ + 2026-04-21 + + + https://www.qpython.org/zh/qsl4a/hardware/recorder/ + 2026-04-21 + + + https://www.qpython.org/zh/qsl4a/hardware/usbserial/ + 2026-04-21 + + + https://www.qpython.org/zh/qsl4a/hardware/webcam/ + 2026-04-21 + + + https://www.qpython.org/zh/qsl4a/media/image/ + 2026-04-21 + + + https://www.qpython.org/zh/qsl4a/media/mediaplayer/ + 2026-04-21 + + + https://www.qpython.org/zh/qsl4a/special/cipher/ + 2026-04-21 + + + https://www.qpython.org/zh/qsl4a/special/pgptai/ + 2026-04-21 + + + https://www.qpython.org/zh/qsl4a/storage/clipboard/ + 2026-04-21 + + + https://www.qpython.org/zh/qsl4a/storage/documentfile/ + 2026-04-21 + + + https://www.qpython.org/zh/qsl4a/storage/preferences/ + 2026-04-21 + + + https://www.qpython.org/zh/qsl4a/system/activityresult/ + 2026-04-21 + + + https://www.qpython.org/zh/qsl4a/system/application/ + 2026-04-21 + + + https://www.qpython.org/zh/qsl4a/system/battery/ + 2026-04-21 + + + https://www.qpython.org/zh/qsl4a/system/qpyinterface/ + 2026-04-21 + + + https://www.qpython.org/zh/qsl4a/system/sensors/ + 2026-04-21 + + + https://www.qpython.org/zh/qsl4a/system/settings/ + 2026-04-21 + + + https://www.qpython.org/zh/qsl4a/system/sysinfo/ + 2026-04-21 + + + https://www.qpython.org/zh/qsl4a/system/wakelock/ + 2026-04-21 + + + https://www.qpython.org/zh/qsl4a/ui/accessibility/ + 2026-04-21 + + + https://www.qpython.org/zh/qsl4a/ui/dialogs/ + 2026-04-21 + + + https://www.qpython.org/zh/qsl4a/ui/floatview/ + 2026-04-21 + + + https://www.qpython.org/zh/qsl4a/ui/fullscreen/ + 2026-04-21 + + \ No newline at end of file diff --git a/zh/sitemap.xml.gz b/zh/sitemap.xml.gz new file mode 100644 index 0000000..f636a48 Binary files /dev/null and b/zh/sitemap.xml.gz differ diff --git a/source/zh/static/1.png b/zh/static/1.png similarity index 100% rename from source/zh/static/1.png rename to zh/static/1.png diff --git a/source/zh/static/2.png b/zh/static/2.png similarity index 100% rename from source/zh/static/2.png rename to zh/static/2.png diff --git a/source/zh/static/3.png b/zh/static/3.png similarity index 100% rename from source/zh/static/3.png rename to zh/static/3.png diff --git a/source/zh/static/aipyapp_demo.jpg b/zh/static/aipyapp_demo.jpg similarity index 100% rename from source/zh/static/aipyapp_demo.jpg rename to zh/static/aipyapp_demo.jpg diff --git a/source/zh/static/bestpython.png b/zh/static/bestpython.png old mode 100755 new mode 100644 similarity index 100% rename from source/zh/static/bestpython.png rename to zh/static/bestpython.png diff --git a/source/zh/static/course_aipy_vibecoding.jpg b/zh/static/course_aipy_vibecoding.jpg similarity index 100% rename from source/zh/static/course_aipy_vibecoding.jpg rename to zh/static/course_aipy_vibecoding.jpg diff --git a/source/zh/static/extra.css b/zh/static/extra.css similarity index 100% rename from source/zh/static/extra.css rename to zh/static/extra.css diff --git a/source/zh/static/googledrive-.png b/zh/static/googledrive-.png similarity index 100% rename from source/zh/static/googledrive-.png rename to zh/static/googledrive-.png diff --git a/source/zh/static/googledrive.jpg b/zh/static/googledrive.jpg similarity index 100% rename from source/zh/static/googledrive.jpg rename to zh/static/googledrive.jpg diff --git a/source/zh/static/guide_extend_pic1.png b/zh/static/guide_extend_pic1.png similarity index 100% rename from source/zh/static/guide_extend_pic1.png rename to zh/static/guide_extend_pic1.png diff --git a/source/zh/static/guide_extend_pic2.png b/zh/static/guide_extend_pic2.png similarity index 100% rename from source/zh/static/guide_extend_pic2.png rename to zh/static/guide_extend_pic2.png diff --git a/source/zh/static/guide_helloworld_pic1.png b/zh/static/guide_helloworld_pic1.png similarity index 100% rename from source/zh/static/guide_helloworld_pic1.png rename to zh/static/guide_helloworld_pic1.png diff --git a/source/zh/static/guide_howtostart_pic1.png b/zh/static/guide_howtostart_pic1.png similarity index 100% rename from source/zh/static/guide_howtostart_pic1.png rename to zh/static/guide_howtostart_pic1.png diff --git a/source/zh/static/guide_howtostart_pic2.png b/zh/static/guide_howtostart_pic2.png similarity index 100% rename from source/zh/static/guide_howtostart_pic2.png rename to zh/static/guide_howtostart_pic2.png diff --git a/source/zh/static/guide_howtostart_pic3.png b/zh/static/guide_howtostart_pic3.png similarity index 100% rename from source/zh/static/guide_howtostart_pic3.png rename to zh/static/guide_howtostart_pic3.png diff --git a/source/zh/static/guide_howtostart_pic4.png b/zh/static/guide_howtostart_pic4.png similarity index 100% rename from source/zh/static/guide_howtostart_pic4.png rename to zh/static/guide_howtostart_pic4.png diff --git a/source/zh/static/guide_howtostart_pic5.png b/zh/static/guide_howtostart_pic5.png similarity index 100% rename from source/zh/static/guide_howtostart_pic5.png rename to zh/static/guide_howtostart_pic5.png diff --git a/source/zh/static/guide_ide_qedit4web.png b/zh/static/guide_ide_qedit4web.png old mode 100755 new mode 100644 similarity index 100% rename from source/zh/static/guide_ide_qedit4web.png rename to zh/static/guide_ide_qedit4web.png diff --git a/source/zh/static/guide_ide_qedit4web_choose.png b/zh/static/guide_ide_qedit4web_choose.png similarity index 100% rename from source/zh/static/guide_ide_qedit4web_choose.png rename to zh/static/guide_ide_qedit4web_choose.png diff --git a/source/zh/static/guide_ide_qedit4web_develop.png b/zh/static/guide_ide_qedit4web_develop.png similarity index 100% rename from source/zh/static/guide_ide_qedit4web_develop.png rename to zh/static/guide_ide_qedit4web_develop.png diff --git a/source/zh/static/guide_program_pic1.png b/zh/static/guide_program_pic1.png similarity index 100% rename from source/zh/static/guide_program_pic1.png rename to zh/static/guide_program_pic1.png diff --git a/source/zh/static/img_banner2x.jpg b/zh/static/img_banner2x.jpg similarity index 100% rename from source/zh/static/img_banner2x.jpg rename to zh/static/img_banner2x.jpg diff --git a/source/zh/static/img_logo.png b/zh/static/img_logo.png similarity index 100% rename from source/zh/static/img_logo.png rename to zh/static/img_logo.png diff --git a/source/zh/static/mov_hellolorld.mp4 b/zh/static/mov_hellolorld.mp4 similarity index 100% rename from source/zh/static/mov_hellolorld.mp4 rename to zh/static/mov_hellolorld.mp4 diff --git a/zh/static/qpy-wechat.png b/zh/static/qpy-wechat.png new file mode 100644 index 0000000..f69d2f6 Binary files /dev/null and b/zh/static/qpy-wechat.png differ diff --git a/source/zh/static/qpy_dashboard.jpg b/zh/static/qpy_dashboard.jpg similarity index 100% rename from source/zh/static/qpy_dashboard.jpg rename to zh/static/qpy_dashboard.jpg diff --git a/source/zh/static/qpy_editor.jpg b/zh/static/qpy_editor.jpg similarity index 100% rename from source/zh/static/qpy_editor.jpg rename to zh/static/qpy_editor.jpg diff --git a/source/zh/static/qpy_helloworld.jpg b/zh/static/qpy_helloworld.jpg similarity index 100% rename from source/zh/static/qpy_helloworld.jpg rename to zh/static/qpy_helloworld.jpg diff --git a/zh/static/qpy_wechat.jpg b/zh/static/qpy_wechat.jpg new file mode 100644 index 0000000..de1002a Binary files /dev/null and b/zh/static/qpy_wechat.jpg differ diff --git a/source/zh/static/qpy_wechat.png b/zh/static/qpy_wechat.png similarity index 100% rename from source/zh/static/qpy_wechat.png rename to zh/static/qpy_wechat.png diff --git a/source/zh/static/qpython_graphical_interace_demo.jpg b/zh/static/qpython_graphical_interace_demo.jpg similarity index 100% rename from source/zh/static/qpython_graphical_interace_demo.jpg rename to zh/static/qpython_graphical_interace_demo.jpg diff --git a/zh/static/qpython_qq_group.jpg b/zh/static/qpython_qq_group.jpg new file mode 100644 index 0000000..1fe33a0 Binary files /dev/null and b/zh/static/qpython_qq_group.jpg differ diff --git a/source/zh/static/sl4a.jpg b/zh/static/sl4a.jpg similarity index 100% rename from source/zh/static/sl4a.jpg rename to zh/static/sl4a.jpg diff --git a/source/zh/static/taskerplugin-for-qpython.png b/zh/static/taskerplugin-for-qpython.png similarity index 100% rename from source/zh/static/taskerplugin-for-qpython.png rename to zh/static/taskerplugin-for-qpython.png diff --git a/source/zh/static/terminal_demo.jpg b/zh/static/terminal_demo.jpg similarity index 100% rename from source/zh/static/terminal_demo.jpg rename to zh/static/terminal_demo.jpg diff --git a/zh/static/udemy_aipy_featured.png b/zh/static/udemy_aipy_featured.png new file mode 100644 index 0000000..f626be4 Binary files /dev/null and b/zh/static/udemy_aipy_featured.png differ diff --git a/zh/static/wechat_qpy_office_account.jpg b/zh/static/wechat_qpy_office_account.jpg new file mode 100644 index 0000000..a89b4de Binary files /dev/null and b/zh/static/wechat_qpy_office_account.jpg differ diff --git a/zh/static/wechat_qpy_service_account.jpg b/zh/static/wechat_qpy_service_account.jpg new file mode 100644 index 0000000..a22384a Binary files /dev/null and b/zh/static/wechat_qpy_service_account.jpg differ diff --git a/source/zh/static/wecomdrive-.png b/zh/static/wecomdrive-.png similarity index 100% rename from source/zh/static/wecomdrive-.png rename to zh/static/wecomdrive-.png diff --git a/source/zh/static/wecomdrive.jpg b/zh/static/wecomdrive.jpg similarity index 100% rename from source/zh/static/wecomdrive.jpg rename to zh/static/wecomdrive.jpg diff --git a/zh/static/zhishi-nobg.jpg b/zh/static/zhishi-nobg.jpg new file mode 100644 index 0000000..f14a256 Binary files /dev/null and b/zh/static/zhishi-nobg.jpg differ diff --git a/zh/static/zhishi-nobg.png b/zh/static/zhishi-nobg.png new file mode 100644 index 0000000..f3c0176 Binary files /dev/null and b/zh/static/zhishi-nobg.png differ diff --git a/zh/tutorial-hello-world/index.html b/zh/tutorial-hello-world/index.html new file mode 100644 index 0000000..059ea45 --- /dev/null +++ b/zh/tutorial-hello-world/index.html @@ -0,0 +1,2990 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Hello World 教程 - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + +

编写"Hello World"

+

Hello world

+

hello world

+

好的,在您对 QPython 有了一定的了解之后,让我们在 QPython 中创建我们的第一个程序。显然,它将是 helloworld.py。;)

+

启动 QPython,打开编辑器并输入以下代码:

+
#qpy:quiet
+import androidhelper
+
+droid = androidhelper.Android()
+droid.makeToast('Hello, QPYTHON!')
+
+

毫无疑问,它与其他任何 hello-world 程序并无不同。执行时,它只是在屏幕上显示弹出消息(见顶部截图)。无论如何,这是一个很好的 QPython 程序示例。

+

代码理解

+

它以 import androidhelper 开头——这是 QPython 中最有用的模块,它封装了 Python 中可用的几乎所有与 Android 的接口。任何在 QPython 中开发的脚本都以这个语句开头(至少如果它声称要与用户通信的话)。在此处阅读更多关于 Python 库import 语句 的内容。

+

接下来我们创建一个 droid 对象(实际上是一个类),它对于调用 RPC 函数以与 Android 通信是必要的。

+

我们代码的最后一行调用了这样的函数,droid.makeToast(),它在屏幕上显示一个小的弹出消息("toast")。

+

好的,让我们添加一些更多的功能。让它询问用户名并向用户打招呼。

+

更多示例

+

我们可以使用 dialogGetInput 调用显示一个带有标题、提示、编辑字段和 确定取消 按钮的简单对话框。用以下代码替换您的最后一行代码并将其保存为 hello1.py

+
#qpy:quiet
+import androidhelper
+
+droid = androidhelper.Android()
+respond = droid.dialogGetInput("Hello", "What is your name?")
+
+

好的,我认为它应该返回任何响应,任何用户反应。这就是为什么我写 respond = ...。但这个调用实际上返回什么?让我们检查一下。只需在最后一行后添加 print 语句:

+
#qpy:quiet
+import androidhelper
+
+droid = androidhelper.Android()
+respond = droid.dialogGetInput("Hello", "What is your name?")
+print(respond)
+
+

然后保存并运行它...

+

哎呀!没有打印?别担心。只需拉下通知栏,您就会看到"QPython Program Output: hello1.py"——点击它!

+

如您所见,droid.dialogGetInput() 返回一个包含三个字段的 JSON 对象。我们只需要一个——result,其中包含用户实际输入的内容。

+

让我们添加脚本的反应:

+
#qpy:quiet
+import androidhelper
+
+droid = androidhelper.Android()
+respond = droid.dialogGetInput("Hello", "What is your name?")
+print(respond)
+message = f'Hello, {respond.result}!'
+droid.makeToast(message)
+
+

最后两行(1)格式化消息,(2)以 toast 形式向用户显示消息。如果您仍然不知道 f-string 是什么意思,请参阅 Python 文档

+

哇!它工作了!;)

+

现在我要在那里添加一些逻辑。想一想:如果用户点击 取消 按钮,或者点击 确定 但将输入字段留空,会发生什么?

+

您可以玩这个程序,检查 respond 变量在每种情况下包含什么。

+

首先,我想将用户输入的文本放入一个单独的变量中:name = respond.result。然后我检查它,如果它包含任何真实文本,它将被视为名称并用于问候。否则,将显示另一条消息。将第五行 message = f'Hello, {respond.result}!' 替换为以下代码:

+
name = respond.result
+if name:
+    message = f'Hello, {name}!'
+else:
+    message = "Hey! And you're not very polite, %Username%!"
+
+

使用工具栏上的 <> 按钮来缩进/取消缩进 if 语句中的行(或者只需使用空格/退格键)。您可以在 此处 阅读更多关于 Python 缩进的内容;if 语句在 此处 描述。

+

首先,我们将用户输入放入 name 变量。然后我们检查 name 是否包含任何内容?如果用户留空行并点击 确定,返回值是空字符串 ''。如果按下了 取消 按钮,返回值是 None。在 if 语句中,两者都被视为假。因此,只有当 name 包含任何有意义的内容时,then 语句才会执行,并显示问候语"Hello, ...!"。如果输入为空,用户将看到"Hey! And you're not very polite, %Username%!"消息。

+

好的,这是整个程序:

+
#qpy:quiet
+import androidhelper
+
+droid = androidhelper.Android()
+respond = droid.dialogGetInput("Hello", "What is your name?")
+print(respond)
+name = respond.result
+if name:
+    message = f'Hello, {name}!'
+else:
+    message = "Hey! And you're not very polite, %Username%!"
+droid.makeToast(message)
+
+

运行效果

+ + +

下一步

+

对于 Python 新用户,推荐学习 Python 语法实践 课程来进一步学习 Python,或者在 QPython 精选课程 中寻找想要学习的内容。

+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/zh/whats-new/index.html b/zh/whats-new/index.html new file mode 100644 index 0000000..ee629b4 --- /dev/null +++ b/zh/whats-new/index.html @@ -0,0 +1,3315 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 更新日志 - QPython + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + +

更新日志

+

v4.0.0

+
    +
  • 外部存储访问:用户现在可以将 Python 脚本直接保存到外部存储设备,大大提升了文件管理的灵活性。
  • +
  • QSL4A 功能增强:改进了 QSL4A 的功能。(https://www.qpython.org/en/qsl4a/)
  • +
  • 社区与课程:优化了社区和课程模块,提供更清晰的信息和更便捷的导航,方便用户访问学习资源和获得支持。
  • +
+

v3.9.2 / v3.9.3

+
    +
  • SDK 升级,支持 16 KB 页面大小,提供更流畅的运行环境
  • +
  • 编辑器重大更新,带来更流畅的编辑体验
  • +
  • 其他各种小改进
  • +
+

v3.9.1

+
    +
  • SDK 升级,支持 16 KB 页面大小,提供更流畅的运行环境
  • +
  • 扩展包现在支持 MCP
  • +
  • 错误修复
  • +
+

v3.9.0

+
    +
  • SDK 升级,整合最新的 Android 功能
  • +
  • 内置 Ollama 0.9.5 升级:现在支持在 QPython 中本地运行 Gemma3n 模型,体验更强大的端侧 AI 计算能力
  • +
  • Pygame 游戏开发支持:结合 XServer,您现在可以轻松地在 QPython 上编写和运行 Pygame 游戏,释放您的创造力!
  • +
  • 个性化图标自定义:通过 QPython 设置自定义桌面图标和主题,创建独特的编程环境
  • +
+

v3.8.11

+
    +
  • 内置 Ollama 0.9.5 升级:现在支持在 QPython 中本地运行 Gemma3n 模型,体验更强大的端侧 AI 计算能力
  • +
  • Pygame 游戏开发支持:结合 XServer,您现在可以轻松地在 QPython 上编写和运行 Pygame 游戏,释放您的创造力!
  • +
  • 个性化图标自定义:通过 QPython 设置自定义桌面图标和主题,创建独特的编程环境
  • +
+

v3.8.10

+
    +
  • 内置 Ollama 升级:现在您可以在 QPython 中轻松本地运行 Qwen3 / Gemma3 模型!
  • +
  • 添加了强大的 MCP 库:在 Extensions -> AIPY 中添加了 mcp 库,让您随时随地携带强大的 MCP 功能!
  • +
+

v3.8.9

+

重大更新!AI 编程完全集成到 QPython,让您的编程更轻松!

+
    +
  • 自然语言编程支持:新支持 AIPyApp,在 QPython 中实现自然语言编程。目前处于测试阶段 - 加入我们的社区获取使用说明
  • +
  • 新 QSL4A 功能:添加 notebookOpen 函数,支持自然语言控制打开 Notebook 文件
  • +
  • 内置编辑器升级:增强编辑器功能,支持打开和浏览各种文本文件
  • +
  • 便捷的文件管理:在文件管理器中添加内部存储入口,快速访问您的文件
  • +
+

v3.8.8

+
    +
  • 文件访问权限控制的重要改进,允许用户灵活地通过 QPython 编程启用或禁用外部存储文件的访问
  • +
  • SDK 升级,增强了对新版 Android 的支持和兼容性
  • +
  • 在 Extensions -> AIPY 中添加了 Anthropic 和 Google GenAI 库
  • +
+

v3.8.7

+
    +
  • 在现有 Tkinter 支持基础上,在 Extensions -> Tools 中添加了使用 Turtle 绘制哆啦A梦的示例代码
  • +
  • 优化了手机权限获取流程,提升用户体验和操作便利性
  • +
  • 在 Extensions -> AIPY 中添加了 Google Gen AI 库,方便访问 Gemini Developer API 和 Vertex AI
  • +
+

v3.8.6

+
    +
  • Tkinter 支持:现在支持 Tkinter 库,可通过 XServer 访问。请访问 www.qpython.org 的云盘"Extra"链接下载所需文件
  • +
  • 存储权限更新:为改善用户体验,我们添加了读取手机存储的权限,用于访问存储在其他目录中的 Python 程序
  • +
+

此更新后,QPython 和 QPythonPlus(不在应用商店上架,专为需要更多敏感权限的高级用户设计)将保持同步的版本号。

+

v3.5.2 (2025/2/25)

+

与开源 LLM 部署工具 Ollama 以及 DeepSeek 开发的 DeepSeek 实现无缝集成!

+

功能: +- 零门槛在移动设备上本地运行各种大型语言模型 +- 快速部署 DeepSeek 等尖端 AI 模型 +- 享受简约的 API 调用体验 +- 构建完全离线的智能编程环境

+

能力: +- 直接在手机上加载/管理 LLM 模型 +- 基于本地计算的实时低延迟响应

+

v3.5.0

+
    +
  • Python 升级到 3.12.8
  • +
  • 改进仪表盘,功能更清晰、更人性化
  • +
  • 添加了众多第三方包:PyTorch / Twisted / Scrapy / FastAPI ...
  • +
+

v3.3.8

+
    +
  • 修复了 NumPy 兼容性问题
  • +
+

v3.3.5

+
    +
  • 升级 Python 内核到 3.11.9
  • +
  • 修复了扩展中不显示模块安装状态的 bug
  • +
  • 修复了扩展中删除模块失败的 bug
  • +
  • 修复了其他 bug
  • +
+

v3.3.4

+
    +
  • 添加了 OpenAI/Langchain/APIGPTCloud AI 包
  • +
  • 移除不必要的文件以减少大小
  • +
+

v3.2.5

+
    +
  • 添加了一些 SL4A 函数
  • +
  • 其他错误修复
  • +
+

v3.2.3

+
    +
  • 更新 Python 版本到 3.11.0
  • +
  • 更新 IPython 版本到 8.6.0
  • +
  • 更新 pip 版本到 22.3.1
  • +
  • 更新科学计算包,自动软链接
  • +
  • 减少空间使用
  • +
  • 添加了一些 SL4A 函数 + 其他错误修复
  • +
+

v3.1.8

+
    +
  • 在终端中添加了操作热键
  • +
  • 修复了编辑器在旋转时丢失内容的问题
  • +
  • 修复了其他 bug
  • +
+
+

在 Google Play 下载

+ + + + + + + + + + + + + + + +
+
+ + + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file

下一步

Next Steps