/
// tojestproste — narzędzia developerskie

CapLab czyta
telefon tak jak Ty go widzisz.

Zestaw natywnych pluginów Capacitor dla Androida. Sieć, ekran, bateria, audio, locale, sensory, Bluetooth/NFC i stan systemu — jednym wywołaniem, w czystym JSON-ie, bez próśb o uprawnienia.

Testy obejmują apkę demonstracyjną prezentującą mój autorski zestaw pluginów Capacitor — żadne dane nie są wysyłane na żaden serwer.

caplab@rodin — device readout
{
  "network": { "connectionSummary": "WiFi", "validated": true },
  "screen": { "resolution": "1220×2429", "refreshHz": 120 },
  "battery": { "percent": 86, "charging": "ac" },
  "device": { "model": "2412DPC0AG", "codename": "rodin" }
  // + 6 kolejnych gałęzi, patrz rejestr pluginów niżej
}
Rejestr pluginów

10 pluginów, jedna konwencja

Każdy plugin ma dokładnie jedną metodę get…Info(), bez argumentów, zwraca jeden obiekt JSON podzielony na tematyczne sekcje (np. wifi, capabilities, battery). Kliknij nazwę, żeby zobaczyć pełną sygnaturę, realny response i przykład wywołania.

01
NetworkInfo .get()

Transport, walidacja internetu, link speed, DNS, szczegóły WiFi, operator komórkowy.

network
▸
Sygnatura
NetworkInfo.get() → Promise<NetworkInfoResult>
Użycie
// npm install @tojestproste/caplab-network-info
import { NetworkInfo } from '@tojestproste/caplab-network-info';

const data = await NetworkInfo.get();
console.log(data.connectionSummary); // "WiFi"
Response (realny, Poco X7 Pro / WiFi 6)
{
  "timestamp": 1785703415362,
  "sdkInt": 36,
  "hasActiveNetwork": true,
  "transport": {
    "wifi": true, "cellular": false, "ethernet": false,
    "vpn": false, "bluetooth": false, "wifiAware": false,
    "lowpan": false, "usb": false
  },
  "capabilities": {
    "internet": true, "validated": true, "notMetered": true,
    "notRoaming": true, "notVpn": true, "captivePortal": false,
    "notSuspended": true, "temporarilyNotMetered": false,
    "linkDownstreamKbps": 66421, "linkUpstreamKbps": 260421,
    "signalStrengthDb": -43
  },
  "linkProperties": {
    "interfaceName": "wlan0", "mtu": 0, "domains": "home",
    "dnsServers": ["192.168.1.1", "2a01:110f:4f00:3f00:ba8c:2bff:fe2b:28"],
    "linkAddresses": ["192.168.1.12/24", "..."]
  },
  "wifi": {
    "ssidRaw": "", // wymaga location, patrz notatka niżej
    "bssidRaw": "02:00:00:00:00:00",
    "rssi": -27, "linkSpeedMbps": 2401, "frequencyMHz": 5500,
    "wifiStandard": 6, "signalLevel0to4": 4, "wifiEnabled": true,
    "wifi5GHzSupported": true, "wifi6GHzSupported": true
  },
  "cellular": {
    "networkOperatorName": "Orange", "networkCountryIso": "pl",
    "dataNetworkType": 0, "error": "SecurityException: getDataNetworkTypeForSubscriber"
  },
  "connectionSummary": "WiFi"
}
02
ScreenInfo .get()

Rozdzielczość realna vs. app-usable, gęstość, jasność, refresh rate, cutout/notch, paski systemowe.

screen
▸
Sygnatura
ScreenInfo.get() → Promise<ScreenInfoResult>
Użycie
// npm install @tojestproste/caplab-screen-info
import { ScreenInfo } from '@tojestproste/caplab-screen-info';

const data = await ScreenInfo.get();
console.log(data.systemBars.systemBarsTotalHeightPx); // 283
Response (realny, Poco X7 Pro / 120Hz)
{
  "appUsableResolution": { "widthPx": 1220, "heightPx": 2429 },
  "realResolution": { "widthPx": 1220, "heightPx": 2712 },
  "density": {
    "density": 3.25, "densityDpi": 520, "densityBucket": "xxxhdpi"
  },
  "orientation": "portrait",
  "brightness": { "value0to255": 88, "percent": 35, "automatic": true },
  "refreshRate": { "currentHz": 120, "supportedHz": [30, 60, 90, 120] },
  "colorCapabilities": { "isWideColorGamut": true, "hdrSupported": true },
  "cutout": {
    "present": true, "safeInsetTop": 130,
    "boundingRects": [{ "left": 577, "top": 0, "right": 643, "bottom": 130 }]
  },
  "systemBars": {
    "statusBarHeightPx": 130, "navigationBarHeightLegacyPx": 153,
    "navigationBarInsetsModern": { "bottom": 153 },
    "likelyGestureNavigation": false,
    "systemBarsTotalHeightPx": 283
  }
}
03
DevMode .get()

Opcje deweloperskie, USB debugging, build apki, kto zainstalował paczkę.

devMode
▸
Sygnatura
DevMode.get() → Promise<DevModeResult>
Użycie
// npm install @tojestproste/caplab-dev-mode
import { DevMode } from '@tojestproste/caplab-dev-mode';

const data = await DevMode.get();
console.log(data.appBuildInfo.isDebugBuild); // true
Response (realny, build debug wgrany przez ADB)
{
  "developer": { "developerOptionsEnabled": true },
  "adb": { "usbDebuggingEnabled": true },
  "mockLocation": {
    "note": "Android 12+ usunął globalny switch - per-app wybór, nieodczytywalny."
  },
  "stayAwakeWhileCharging": { "enabled": false, "rawValue": 0 },
  "appBuildInfo": {
    "isDebugBuild": true, "packageName": "com.tojestproste.caplab",
    "versionName": "1.0", "versionCode": 1, "targetSdkVersion": 34,
    "installerPackageName": "adb / sideload (brak installera)"
  },
  "systemBuild": {
    "androidVersion": "16", "sdkInt": 36, "manufacturer": "Xiaomi",
    "fingerprint": "POCO/rodin_eea/rodin:16/BP2A.250605.031.A3/.../release-keys",
    "isEmulator": false
  }
}
04
DeviceInfo .get()

Model, nazwa kodowa, build fingerprint, wersja Androida, patch bezpieczeństwa, RAM, storage, Google Play Services.

device
▸
Sygnatura
DeviceInfo.get() → Promise<DeviceInfoResult>
Użycie
// npm install @tojestproste/caplab-device-info
import { DeviceInfo } from '@tojestproste/caplab-device-info';

const data = await DeviceInfo.get();
console.log(data.identity.codename); // "rodin"
Response (realny, Poco X7 Pro)
{
  "identity": {
    "manufacturer": "Xiaomi", "brand": "POCO", "model": "2412DPC0AG",
    "device": "rodin", "codename": "rodin", "hardware": "mt6899"
  },
  "build": {
    "fingerprint": "POCO/rodin_eea/rodin:16/BP2A.250605.031.A3/.../release-keys",
    "type": "user", "tags": "release-keys"
  },
  "androidVersion": { "release": "16", "sdkInt": 36, "codename": "REL" },
  "security": { "securityPatchLevel": "2026-06-01" },
  "googlePlayServices": {
    "installed": true, "versionName": "26.29.31 (260400-952280416)"
  },
  "memory": {
    "totalRamGB": 11.05, "availableRamGB": 3.53, "isLowRamDevice": false
  },
  "storage": {
    "totalGB": 479.35, "availableGB": 206.8, "usedPercent": 57
  },
  "cpu": { "supportedAbis": ["arm64-v8a"], "numberOfCores": 8 },
  "uptime": { "uptimeHours": 107.97 }
}
05
BatteryInfo .get()

Poziom, status ładowania, typ ładowarki, temperatura, health, napięcie, technologia ogniwa.

battery
▸
Sygnatura
BatteryInfo.get() → Promise<BatteryInfoResult>
Użycie
// npm install @tojestproste/caplab-battery-info
import { BatteryInfo } from '@tojestproste/caplab-battery-info';

const data = await BatteryInfo.get();
console.log(data.level.percent); // 86
Response (realny, ładowanie AC)
{
  "level": { "percent": 86, "rawLevel": 86, "rawScale": 100 },
  "charging": {
    "isCharging": true, "statusName": "charging", "plugType": "ac",
    "usbCharge": false, "acCharge": true, "wirelessCharge": false
  },
  "health": { "code": 2, "name": "good" },
  "temperature": { "rawTenthsOfCelsius": 350, "celsius": 35 },
  "voltage": { "millivolts": 4307, "volts": 4.31 },
  "technology": "Li-poly", "batteryPresent": true,
  "batteryManagerExtra": {
    "capacityPercentFromManager": 86, "chargeCounterMicroAh": 5016000,
    "currentNowMicroA": -30000
  }
}
06
LocaleInfo .get()

Język, region, strefa czasowa, tryb ciemny/jasny systemowy, format daty/liczb, kierunek pisma.

locale
▸
Sygnatura
LocaleInfo.get() → Promise<LocaleInfoResult>
Użycie
// npm install @tojestproste/caplab-locale-info
import { LocaleInfo } from '@tojestproste/caplab-locale-info';

const data = await LocaleInfo.get();
console.log(data.uiMode.isDarkModeActive); // false
Response (realny, pl-PL, motyw jasny)
{
  "locale": {
    "language": "pl", "country": "PL", "displayName": "polski (Polska)",
    "toLanguageTag": "pl-PL", "allSystemLocales": ["pl-PL"]
  },
  "timezone": {
    "id": "Europe/Warsaw", "rawOffsetHours": 1, "currentlyInDST": true
  },
  "uiMode": {
    "nightMode": "light", "isDarkModeActive": false,
    "uiModeTypeName": "vendor_extension_15"
  },
  "formatting": {
    "sampleDateFormatted": "niedziela, 2 sierpnia 2026, 21:53",
    "decimalSeparator": ",", "currencySymbol": "zł", "is24HourFormat": true
  },
  "layoutDirection": { "isRtl": false }
}
07
SensorInfo .get()

Pełna lista fizycznych czujników — nazwa, vendor, zasięg, pobór mocy. Bez odczytu danych na żywo.

osobne wywołanie
▸
Sygnatura
SensorInfo.get() → Promise<SensorInfoResult>
Użycie
// npm install @tojestproste/caplab-sensor-info
import { SensorInfo } from '@tojestproste/caplab-sensor-info';

const data = await SensorInfo.get();
console.log(data.totalSensorCount); // 43
console.log(data.commonSensorsPresence.stepCounter); // true
Response (realny, 43 czujniki na Poco X7 Pro — wycinek)
{
  "totalSensorCount": 43,
  "sensors": [
    {
      "name": "lsm6dsvq_acc", "vendor": "st", "stringType": "android.sensor.accelerometer",
      "maxRange": 78.45, "power": 0.001, "reportingMode": "continuous"
    },
    { "name": "tsl2522_als", "stringType": "android.sensor.light", "reportingMode": "on_change" },
    { "name": "Fod Wakeup", "vendor": "xiaomi", "stringType": "xiaomi.sensor.fod" }
    // ... pozostałe 40 czujników
  ],
  "commonSensorsPresence": {
    "accelerometer": true, "gyroscope": true, "proximity": true,
    "stepCounter": true, "heartRate": false
  }
}
Uwaga

Celowo poza domyślnym scalonym JSON-em w apce testowej — 43 czujniki potrafią przekroczyć limit schowka telefonu. Plugin jest w pełni gotowy, wywołaj go osobno.

08
AudioInfo .get()

Głośność per-stream, tryb dzwonienia, wyciszenie mikrofonu, podłączone urządzenia wyjściowe.

audio
▸
Sygnatura
AudioInfo.get() → Promise<AudioInfoResult>
Użycie
// npm install @tojestproste/caplab-audio-info
import { AudioInfo } from '@tojestproste/caplab-audio-info';

const data = await AudioInfo.get();
console.log(data.volumes.music.percent); // np. 60
Response (kształt danych)
{
  "volumes": {
    "music": { "current": 9, "max": 15, "percent": 60, "isMuted": false },
    "ring": { "current": 5, "max": 7, "percent": 71 },
    "alarm": { "current": 6, "max": 7 }
  },
  "ringerMode": { "modeCode": 2, "modeName": "normal" },
  "audioMode": { "code": 0, "name": "normal" },
  "isMicrophoneMute": false, "isSpeakerphoneOn": false,
  "audioDevices": {
    "wiredHeadsetConnected": false, "bluetoothA2dpOn": false,
    "outputDevices": [{ "type": "builtin_speaker", "isSink": true }]
  },
  "flags": { "musicActive": false, "volumeFixed": false }
}
09
PowerState .get()

Power save mode, Doze/idle, czy ekran interaktywny, zwolnienie z optymalizacji baterii.

powerState
▸
Sygnatura
PowerState.get() → Promise<PowerStateResult>
Użycie
// npm install @tojestproste/caplab-power-state
import { PowerState } from '@tojestproste/caplab-power-state';

const data = await PowerState.get();
console.log(data.batteryOptimization.isExemptFromOptimization);
Response (kształt danych)
{
  "powerSave": { "isPowerSaveMode": false, "locationPowerSaveMode": 0 },
  "doze": { "isDeviceIdleMode": false, "isInteractive": true },
  "screenState": { "isInteractive": true },
  "batteryOptimization": {
    "isExemptFromOptimization": false,
    "note": "Apka podlega standardowym ograniczeniom Doze/App Standby."
  },
  "supportsSustainedPerformanceMode": true
}
10
ConnectivityState .get()

Stan Bluetooth (on/off, adapter) i NFC (obecność, on/off). Bez skanowania/parowania.

connectivity
▸
Sygnatura
ConnectivityState.get() → Promise<ConnectivityStateResult>
Użycie
// npm install @tojestproste/caplab-connectivity-state
import { ConnectivityState } from '@tojestproste/caplab-connectivity-state';

const data = await ConnectivityState.get();
console.log(data.bluetooth.isEnabled);
Response (kształt danych)
{
  "bluetooth": {
    "hardwareSupported": true, "isEnabled": true,
    "stateName": "on",
    "adapterNameError": "Wymaga BLUETOOTH_CONNECT (API 31+)"
  },
  "nfc": { "hardwareSupported": true, "isEnabled": true }
}
O uprawnieniach

Wszystkie 10 pluginów działa na ACCESS_NETWORK_STATE i ACCESS_WIFI_STATE — obie to uprawnienia „normal”, przyznawane automatycznie przy instalacji, bez dialogu. Efekt uboczny: wifi.ssidRaw zawsze wróci puste (system wymaga lokalizacji, żeby pokazać nazwę sieci od Androida 8). To ograniczenie systemowe, nie błąd pluginu.

Wypróbuj na telefonie

CapLab Plugin Demo na Google Play

Apka demonstracyjna konsumuje wszystkie 10 pluginów jako realne zależności npm (nie lokalne pliki Java) — najprostszy sposób, żeby zobaczyć każdy plugin w działaniu bez pisania linijki kodu.

1

Dołącz do grupy testerów

Google wymaga bycia w grupie, żeby zobaczyć apkę w testach zamkniętych.

⚠ Po otwarciu strony grupy kliknij przycisk „Dołącz do grupy” (Join) — samo wejście na stronę nie wystarczy.

Otwórz grupę testerów
2

Zaakceptuj zaproszenie do testów

Link opt-in aktywuje Twoje konto Google do pobrania wersji testowej.

Dołącz do testów
3

Zainstaluj z Google Play

Po aktywacji apka pojawi się jak każda inna, ze stałymi aktualizacjami.

⚠ Jeśli apka nie pojawia się od razu, poczekaj — Google Play potrzebuje czasem od kilkunastu minut do 2–3 godzin, żeby udostępnić dostęp po dołączeniu do testów.

Otwórz w Google Play
Status

Apka jest w fazie testów zamkniętych (closed testing) — liczba miejsc jest ograniczona przez Google, a każdy krok powyżej jest wymagany w tej kolejności. Jeśli krok 3 pokazuje błąd braku dostępu, wróć do kroku 1 lub 2 i odczekaj chwilę.

Szybki start

Od instalacji do JSON-a

1

Zainstaluj plugin z npm

npm install @tojestproste/caplab-xxx, potem npx cap sync android — Capacitor rejestruje plugin automatycznie, bez ręcznej edycji MainActivity.java.

2

Zaimportuj i wywołaj

Standardowy import ES module — każdy plugin eksportuje jedną nazwaną klasę z metodą get().

3

Zbuduj jak zwykłą apkę Capacitor

gradlew assembleDebug → adb install -r app-debug.apk, albo standardowy release build pod Google Play.

Instalacja
npm install @tojestproste/caplab-network-info @tojestproste/caplab-battery-info
npx cap sync android
index.html — wywołanie pojedynczego pluginu
import { BatteryInfo } from '@tojestproste/caplab-battery-info';

const data = await BatteryInfo.get();
console.log(data.level.percent);      // 86
console.log(data.charging.plugType); // "ac"
index.html — scalanie kilku pluginów naraz
import { NetworkInfo } from '@tojestproste/caplab-network-info';
import { ScreenInfo } from '@tojestproste/caplab-screen-info';
import { BatteryInfo } from '@tojestproste/caplab-battery-info';

const [network, screen, battery] = await Promise.all([
  NetworkInfo.get(),
  ScreenInfo.get(),
  BatteryInfo.get()
]);

const merged = { network, screen, battery };
document.getElementById('output').textContent = JSON.stringify(merged, null, 2);
FAQ

Najczęstsze pytania

Czym jest CapLab?▸
Zestaw 10 niezależnych pluginów Capacitor dla Androida, każdy odczytujący jeden obszar stanu urządzenia (sieć, ekran, bateria, sensory, audio, itd.) i zwracający wynik jako czytelny JSON. Każdy plugin jest osobnym pakietem npm pod @tojestproste/caplab-*, więc możesz zainstalować tylko te, których potrzebujesz.
Czy pluginy wymagają specjalnych uprawnień?▸
Nie — 9 z 10 pluginów działa bez żadnego wpisu w AndroidManifest.xml. Jedyny wyjątek to NetworkInfo, który wymaga ACCESS_NETWORK_STATE i ACCESS_WIFI_STATE — obie to uprawnienia „normal”, przyznawane automatycznie przy instalacji, bez dialogu proszącego użytkownika o zgodę. Świadomie pominięte funkcje (np. realny SSID WiFi, dokładny sygnał komórkowy) wymagałyby dodatkowego dostępu do lokalizacji lub telefonu — patrz notatka w rejestrze pluginów wyżej.
Jak mogę przetestować apkę demo?▸
Trzy kroki w sekcji Wypróbuj na telefonie wyżej: dołącz do grupy testerów, zaakceptuj zaproszenie opt-in, zainstaluj z Google Play. Apka jest obecnie w fazie testów zamkniętych — liczba miejsc jest ograniczona przez Google, więc kolejność kroków ma znaczenie, a dostęp bywa aktywny dopiero po kilkunastu minutach do kilku godzin.
Czy CapLab jest darmowy i open source?▸
Tak. Wszystkie pluginy są publikowane na npm na licencji MIT — możesz je używać, modyfikować i redystrybuować bez opłat. Apka demo na Google Play jest bezpłatna, bez reklam i bez zbierania danych — to czysto techniczne demo, nie produkt komercyjny.
Czy mogę użyć tylko jednego pluginu, bez reszty?▸
Tak — to był jeden z głównych celów podziału na 10 osobnych pakietów. npm install @tojestproste/caplab-battery-info instaluje wyłącznie ten jeden plugin, bez ciągnięcia pozostałych dziewięciu.
Czy działa na iOS?▸
Nie na razie — wszystkie 10 pluginów jest zaimplementowanych wyłącznie natywnie po stronie Androida (Java). Wywołanie na iOS lub w przeglądarce zwróci czytelny błąd „dostępny tylko na Androidzie” zamiast fałszywych danych.