Odczytywanie stanu urządzenia Rozpoczynamy od gotowego projektu dla API 15 (Android 4.0.3) - w API niższym niż API 5 brakuje niektórych klas odpowiedzialnych za detekcję stanu baterii i detekcji dodatkowych urządzeń (będzie zaznaczone w tekście) My będziemy używać rzeczy z API 21 W tym projekcie jest wywoływanie aktywności z innej aplikacji (lista procesów) W tym projekcie zdefiniowany jest osobny wątek, który co dwie sekundy uruchamia metodę odswiezListe, która zbiera i wyświetla informacje o urządzeniach. 0. Zwiększmy wymagane API - do pliku manifestu dodajemy Prawy klawisz na "app" Open Module Settings (F4 przy aktywnym eksploratorze plików) Przejdź na zakładkę Flavors Min Sdk Version: >= 21 1. Zacznijmy od pobrania wersji systemu. a. Zdefiniujmy funkcję: static private UrzadzenieInfo odczytajWersjęSystemu(Activity activity) { UrzadzenieInfo systemInfo = new UrzadzenieInfo(); systemInfo.nazwa="Wersja systemu"; systemInfo.typInformacji=TypInformacji.Inne; systemInfo.polozeniePaska = -1; //systemInfo.opis="Bieżąca data i czas: " + new Date().toLocaleString(); //deprecated systemInfo.opis="Bieżąca data i czas: " + DateFormat.getDateInstance().format(new Date()); systemInfo.opis+="\nProducent: " + Build.MANUFACTURER; systemInfo.opis+="\nUrządzenie: " + Build.DEVICE; systemInfo.opis+="\nWersja: " + Build.VERSION.RELEASE + " (" + Build.VERSION.INCREMENTAL + ")"; //systemInfo.opis+="\nTyp CPU: " + Build.CPU_ABI; //deprecated systemInfo.opis+="\nTyp CPU: "; for(String abi : Build.SUPPORTED_ABIS) //wymaga API 21 { systemInfo.opis+="\n\t" + abi; } systemInfo.opis+="\nWyświetlacz: " + Build.DISPLAY; return systemInfo; } b. Wywołanie tej funkcji dodajmy do metody odswiezListe: private void odswiezListe() { int pozycja = listView.getFirstVisiblePosition(); View v = listView.getChildAt(0); int top = (v==null)?0:v.getTop(); ArrayList listaUrzadzen = new ArrayList(); //tu dodawaj opisy stanu kolejnych urzadzen listaUrzadzen.add(odczytajWersjęSystemu(this)); UrzadzeniaAdapter a=new UrzadzeniaAdapter(this,R.layout.wiersz,listaUrzadzen); listView.setAdapter(a); listView.setSelectionFromTop(pozycja, top); } 2. Pamięć - nie ma jednoznacznego źródła co należy w Androidzie (Linuxie) traktować jako pamięć wolną. System dąży do maksymalnego wykorzystania pamięci (pamięć wolna to pamięć stracona) na wszelkiego typu bufory (przyspieszenie działania). Aplikacje usuwane są z pamięci dopiero, gdy jest to konieczne (przyspieszenie ponownego uruchamiania/przywracania). Ilość dostępnej pamięci można pobrać z ActivityManager.getMemoryInfo(mi) - niejednoznaczna jej interpretacja Całkowita - z pliku /proc/meminfo - pierwsza linia zawiera "MemTotal: 739300 kB" Ta funkcja jest już zdefiniowana w projekcie: public static synchronized int readRAMTotalSizeKB() { try { RandomAccessFile reader = new RandomAccessFile("/proc/meminfo", "r"); String s = reader.readLine(); reader.close(); String[] totrm = s.split(" kB",0); String[] trm = totrm[0].split(" "); int tm = 0; tm = Integer.parseInt(trm[trm.length - 1]); return tm; } catch (IOException ex) { ex.printStackTrace(); return -1; } } Przykładowa zawartość pliku /proc/meminfo na Samsung Galaxcy Tab 10.1 MemTotal: 739300 kB MemFree: 9200 kB Buffers: 5144 kB Cached: 136256 kB SwapCached: 0 kB Active: 541176 kB Inactive: 88560 kB itd. Zasadnicza metoda odczytująca stan pamięci: static private UrzadzenieInfo odczytajStanPamieciRAM(Activity activity) { //Pamięć wewnętrzna (RAM) UrzadzenieInfo pamiecInfo = new UrzadzenieInfo(); pamiecInfo.typInformacji=TypInformacji.StanUrzadzenia; pamiecInfo.nazwa = "Pamięć (RAM)"; MemoryInfo mi = new MemoryInfo(); ActivityManager activityManager = (ActivityManager) activity.getSystemService(ACTIVITY_SERVICE); activityManager.getMemoryInfo(mi); long pamiecDostepnaMB = mi.availMem / 1024; int pamiecCalkowitaMB = readRAMTotalSizeKB(); long pamiecZajetaMB = pamiecCalkowitaMB-pamiecDostepnaMB; pamiecInfo.opis="Dostępna pamięć: " + pamiecDostepnaMB + "/" + pamiecCalkowitaMB + " kB"; pamiecInfo.polozeniePaska = (int)(pamiecZajetaMB*100/pamiecCalkowitaMB); return pamiecInfo; } Jej wywołanie należy dodać do oswiezListe. 3. Karty pamięci (główna i dodatkowa) - inaczej niż pamięć RAM, bo jest na nich system plików, z którego możemy skorzystać. Klasa StatFS. Obie funkcje niemal takie same Zasadnicza metoda odczytująca stan wskazanej katalogiem pamięci i zapisująca informacje do podanego obiektu UrzadzenieInfo: static private void odczytajStanPamięci(Activity activity, File memoryDirectory, UrzadzenieInfo kartaPamięciInfo) { StatFs systemPlikow = new StatFs(memoryDirectory.getPath()); long kartaPamieciWolneMiejsce = (systemPlikow.getAvailableBlocksLong() * systemPlikow.getBlockSizeLong()) / 1024/1024; long kartaPamieciRozmiar = (systemPlikow.getBlockCountLong() * systemPlikow.getBlockSizeLong()) / 1024/1024; long kartaPamieciZajeteMiejsce = kartaPamieciRozmiar - kartaPamieciWolneMiejsce; kartaPamięciInfo.opis = "Ścieżka: " + Environment.getRootDirectory().getPath() + "\n"; kartaPamięciInfo.opis += "Wolne miejsce: "+ kartaPamieciWolneMiejsce+"/"+kartaPamieciRozmiar + " MB"; kartaPamięciInfo.polozeniePaska = (int)(kartaPamieciZajeteMiejsce*100/kartaPamieciRozmiar); } Dwie funkcje wykorzystujące poprzednią i odczytujące ilość pamięci głównej i karty pamięci: static private UrzadzenieInfo odczytajStanGlownejKartyPamieci(Activity activity) { UrzadzenieInfo kartaPamięciInfo = new UrzadzenieInfo(); kartaPamięciInfo.typInformacji = TypInformacji.StanUrzadzenia; kartaPamięciInfo.nazwa = "Główna karta pamięci"; odczytajStanPamięci(activity, Environment.getRootDirectory(), kartaPamięciInfo); return kartaPamięciInfo; } static private UrzadzenieInfo odczytajStanDodatkowejKartyPamieci(Activity activity) { UrzadzenieInfo kartaPamięciInfo = new UrzadzenieInfo(); kartaPamięciInfo.typInformacji = TypInformacji.StanUrzadzenia; kartaPamięciInfo.nazwa = "Dodatkowa karta pamięci"; if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { odczytajStanPamięci(activity, Environment.getExternalStorageDirectory(), kartaPamięciInfo); } else { kartaPamięciInfo.opis = "Brak"; kartaPamięciInfo.polozeniePaska=-1; } return kartaPamięciInfo; } Ich wywołanie należy dodać do metody oswiezListe. 4. Procesor (obciazenie) - pobieranie z plików /proc/cpuinfo - parametry /proc/stat - bieżący stan static private UrzadzenieInfo odczytajStanProcesoraCPU(Activity activity) { //CPU UrzadzenieInfo procesorInfo = new UrzadzenieInfo(); procesorInfo.typInformacji = TypInformacji.StanUrzadzenia; procesorInfo.nazwa = "Procesor (CPU)"; int procesorObciarzenie = Math.round(100*readCPUUsage()); String opisProcesora = readCPUInfo(); procesorInfo.opis = opisProcesora + "\n"; procesorInfo.opis += "Obciążenie procesora: " + procesorObciarzenie + "%"; procesorInfo.polozeniePaska = procesorObciarzenie; return procesorInfo; } Te funkcje są już zdefiniowane: //http://stackoverflow.com/questions/3118234/how-to-get-memory-usage-and-cpu-usage-in-android private static float readCPUUsage() { try { RandomAccessFile reader = new RandomAccessFile("/proc/stat", "r"); String load = reader.readLine(); String[] toks = load.split(" "); long idle1 = Long.parseLong(toks[5]); long cpu1 = Long.parseLong(toks[2]) + Long.parseLong(toks[3]) + Long.parseLong(toks[4]) + Long.parseLong(toks[6]) + Long.parseLong(toks[7]) + Long.parseLong(toks[8]); try { Thread.sleep(360); } catch (Exception e) {} reader.seek(0); load = reader.readLine(); reader.close(); toks = load.split(" "); long idle2 = Long.parseLong(toks[5]); long cpu2 = Long.parseLong(toks[2]) + Long.parseLong(toks[3]) + Long.parseLong(toks[4]) + Long.parseLong(toks[6]) + Long.parseLong(toks[7]) + Long.parseLong(toks[8]); return (float)(cpu2 - cpu1) / ((cpu2 + idle2) - (cpu1 + idle1)); } catch (IOException ex) { ex.printStackTrace(); } return 0; } //http://www.roman10.net/how-to-get-cpu-information-on-android/ private static String readCPUInfo() { StringBuffer sb = new StringBuffer(); //sb.append("abi: ").append(Build.CPU_ABI).append("\n"); if (new File("/proc/cpuinfo").exists()) { try { BufferedReader br = new BufferedReader(new FileReader(new File("/proc/cpuinfo"))); String line; while ((line = br.readLine()) != null) { sb.append(line + "\n"); } if (br != null) { br.close(); } } catch (IOException e) { e.printStackTrace(); } } return sb.toString(); } 5. Bateria - można zareestrować nasłuchiwacza i być powiadamianym o zmianach stanu. a. Definiujemy pole BroadcastReceiver batteryReceiver = null; b. Inicjujemy je i definiujemy intent (intencję) w metodzie odswiezListe. Wywolujemy tez metode odczytajStanBaterii, której jeszcze nie ma: private void odswiezListe() { int pozycja = listView.getFirstVisiblePosition(); View v = listView.getChildAt(0); int top = (v==null)?0:v.getTop(); ArrayList listaUrzadzen = new ArrayList(); listaUrzadzen.add(odczytajWersjeSystemu(this)); ... if(batteryReceiver == null) batteryReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { int bateriaPoziom = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1); if(bateriaPoziom<10) Toast.makeText(context, "Niski stan baterii", Toast.LENGTH_LONG).show(); //odswiezListe(); } }; IntentFilter intentFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); Intent intent = this.getApplicationContext().registerReceiver(batteryReceiver, intentFilter); listaUrzadzen.add(odczytajStanBaterii(this, intent)); ... } c. Wreszcie definiujemy funkcję odczytującą stan baterii: static private UrzadzenieInfo odczytajStanBaterii(Activity activity, Intent intent) { //Bateria (API 5) UrzadzenieInfo bateriaInfo = new UrzadzenieInfo(); bateriaInfo.typInformacji=TypInformacji.StanUrzadzenia; bateriaInfo.nazwa="Bateria"; if (intent.getBooleanExtra(BatteryManager.EXTRA_PRESENT, false)) { String bateriaTechnologia = intent.getStringExtra(BatteryManager.EXTRA_TECHNOLOGY); int bateriaStan = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1); String bateriaStanOpis = ""; switch(bateriaStan) { case BatteryManager.BATTERY_STATUS_CHARGING: bateriaStanOpis = "ładowanie"; break; case BatteryManager.BATTERY_STATUS_DISCHARGING: bateriaStanOpis = "rozładowywanie"; break; case BatteryManager.BATTERY_STATUS_FULL: bateriaStanOpis = "w pełni naładowana"; break; case BatteryManager.BATTERY_STATUS_NOT_CHARGING: bateriaStanOpis = "nie ładowana"; break; default: case BatteryManager.BATTERY_STATUS_UNKNOWN: bateriaStanOpis = "nieznany"; break; } int bateriaMetodaLadowania = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1); String bateriaMetodaLadowaniaOpis = ""; switch (bateriaMetodaLadowania) { case BatteryManager.BATTERY_PLUGGED_AC: bateriaMetodaLadowaniaOpis = "AC"; break; case BatteryManager.BATTERY_PLUGGED_USB: bateriaMetodaLadowaniaOpis = "USB"; break; default: bateriaMetodaLadowaniaOpis = "-"; break; } int bateriaSkala = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1); int bateriaPoziom = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1); int bateriaNapiecie = intent.getIntExtra(BatteryManager.EXTRA_VOLTAGE, -1); int bateriaTemperatura = intent.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, -1); bateriaInfo.opis="Technologia: " + bateriaTechnologia; bateriaInfo.opis += "\nStan: " + bateriaStanOpis + " (" + bateriaMetodaLadowaniaOpis + ")"; bateriaInfo.opis += "\nPoziom naładowania: "+bateriaPoziom + "/" + bateriaSkala; bateriaInfo.opis += "\nNapięcie: "+bateriaNapiecie/1000 + " V"; bateriaInfo.opis += "\nTemperatura: " + bateriaTemperatura/10 + " °C"; bateriaInfo.polozeniePaska = bateriaPoziom*100/bateriaSkala; } else { bateriaInfo.opis="Brak"; bateriaInfo.polozeniePaska=-1; } return bateriaInfo; } d. W metodzie onPause wyrejestrowujemy powiadamianie o zmianie stanu baterii: @Override protected void onPause() { super.onPause(); if(batteryReceiver!=null) this.getApplicationContext().unregisterReceiver(batteryReceiver); } e. Do zmiany stanu baterii w emulatorze można użyć połączenia via telnet z localhost na porcie wskazywanym w nazwie emulatora (pasek tytułu) tj. Zajrzyj do pliku 'C:\Users\jacek\.emulator_console_auth_token' > telnet localhost 5554 help auth F6zNKvn+CHDHJLgi (kod wzięty z powyższego pliku) power status discharging power capacity 30 (wszelkie pomyłki i kasowania znaku -> pisanie linii od nowa) Uwaga! Program telnet jest częścią Windows, ale nie musi być zainstalowany (Panel sterowania > Programy > Włącz lub wyłącz funkcje systemu Windows > Klient Telnet). Można go włączyć z okna Terminal w Android Studio 6. Lista dodatkowych urządzeń (wymaga API 5): //API 5 static private UrzadzenieInfo odczytajObecnoscDodatkowychUrzadzen(Activity activity) { //Dodatkowe urządzenia UrzadzenieInfo dodatkoweInfo = new UrzadzenieInfo(); dodatkoweInfo.typInformacji=TypInformacji.Inne; dodatkoweInfo.nazwa="Dodatkowe urządzenia"; dodatkoweInfo.polozeniePaska=-1; PackageManager pm = activity.getPackageManager(); boolean czyTelefonObecny = pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY); dodatkoweInfo.opis = "Telefon: " + (czyTelefonObecny?"obecny":"brak"); if(czyTelefonObecny) { dodatkoweInfo.opis = pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_GSM)?" (GSM)":""; dodatkoweInfo.opis = pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_CDMA)?" (CDMA)":""; } boolean czyKameraObecna = pm.hasSystemFeature(PackageManager.FEATURE_CAMERA); dodatkoweInfo.opis += "\nAparat fotograficzny/kamera: " + (czyKameraObecna?"obecna":"brak"); if(czyKameraObecna) { dodatkoweInfo.opis += "\n\tautomatyczna regulacja ostrości: " + (pm.hasSystemFeature(PackageManager.FEATURE_CAMERA_AUTOFOCUS)?"obecna":"brak"); dodatkoweInfo.opis += "\n\tlampa błyskowa: " + (pm.hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH)?"obecna":"brak"); } dodatkoweInfo.opis += "\nCzujnik światła: " + (pm.hasSystemFeature(PackageManager.FEATURE_SENSOR_LIGHT)?"obecny":"brak"); dodatkoweInfo.opis += "\nCzujnik zbliżeniowy: " + (pm.hasSystemFeature(PackageManager.FEATURE_SENSOR_PROXIMITY)?"obecny":"brak"); dodatkoweInfo.opis += "\nEkran wielodotykowy: " + (pm.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH)?"obecny":"brak"); dodatkoweInfo.opis += "\nAnimowana tapeta: " + (pm.hasSystemFeature(PackageManager.FEATURE_LIVE_WALLPAPER)?"obecna":"brak"); return dodatkoweInfo; } 7. [POMINĄĆ] Stan audio (NIE DZIAŁA PRZEZ ZDALNY PULPIT!): static private UrzadzenieInfo odczytajStanAudio(Activity activity) { //Stan audio UrzadzenieInfo audioInfo = new UrzadzenieInfo(); audioInfo.typInformacji=TypInformacji.Inne; audioInfo.nazwa="Audio"; audioInfo.polozeniePaska=-1; AudioManager am = (AudioManager) activity.getSystemService(Context.AUDIO_SERVICE); int trybAudio = am.getMode(); String trybAudioOpis = "Tryb audio: "; switch(trybAudio) { case AudioManager.MODE_NORMAL: trybAudioOpis += "normalny"; break; case AudioManager.MODE_RINGTONE: trybAudioOpis += "dzwonki"; break; case AudioManager.MODE_IN_CALL: trybAudioOpis += "w trakcie rozmowy"; break; default: trybAudioOpis += "niezanany"; break; } audioInfo.opis = trybAudioOpis; int trybDzwonka = am.getRingerMode(); String trybDzwonkaOpis = "Rodzaj dzwonka: "; switch(trybDzwonka) { case AudioManager.RINGER_MODE_NORMAL: trybDzwonkaOpis += "normalny"; break; case AudioManager.RINGER_MODE_SILENT: trybDzwonkaOpis += "wyciszony"; break; case AudioManager.RINGER_MODE_VIBRATE: trybDzwonkaOpis += "wibracje"; break; default: trybDzwonkaOpis += "niezanany"; break; } audioInfo.opis += "\n" + trybDzwonkaOpis; audioInfo.opis += "\nWyciszenie mikrofonu: " + (am.isMicrophoneMute()?"tak":"nie"); audioInfo.opis += "\nOdtwarzanie muzyki: " + (am.isMusicActive()?"tak":"nie"); return audioInfo; } 8. Odczytać listę czujników/sensorów (NIE DZIAŁA PRZEZ ZDALNY PULPIT!): static private String opisCzujnika(List lista,String nazwa) { String opis=nazwa+": "; if(lista.size()>1) opis+="\n"; if(lista.size()>0) for(int i=0;i1) opis+="\t"; Sensor czujnik = lista.get(i); opis+=czujnik.getName()+", wersja: "+czujnik.getVersion()+"\n"; } else opis+="brak\n"; return opis; } static private UrzadzenieInfo odczytajObecnoscCzujnikow(Activity activity) { //Dodatkowe urządzenia UrzadzenieInfo czujnikiInfo = new UrzadzenieInfo(); czujnikiInfo.typInformacji=TypInformacji.Inne; czujnikiInfo.nazwa="Czujniki"; czujnikiInfo.polozeniePaska=-1; SensorManager sm = (SensorManager) activity.getSystemService(Context.SENSOR_SERVICE); czujnikiInfo.opis = opisCzujnika(sm.getSensorList(Sensor.TYPE_ACCELEROMETER), "Akcelerator"); czujnikiInfo.opis += opisCzujnika(sm.getSensorList(Sensor.TYPE_GYROSCOPE), "Żyroskop"); czujnikiInfo.opis += opisCzujnika(sm.getSensorList(Sensor.TYPE_LIGHT), "Światło"); czujnikiInfo.opis += opisCzujnika(sm.getSensorList(Sensor.TYPE_MAGNETIC_FIELD), "Pole magnetyczne"); //czujnikiInfo.opis += opisCzujnika(sm.getSensorList(Sensor.TYPE_ORIENTATION), "Orientacja"); czujnikiInfo.opis += opisCzujnika(sm.getSensorList(Sensor.TYPE_PRESSURE), "Ciśnienie"); czujnikiInfo.opis += opisCzujnika(sm.getSensorList(Sensor.TYPE_PROXIMITY), "Odległość"); czujnikiInfo.opis += opisCzujnika(sm.getSensorList(Sensor.TYPE_AMBIENT_TEMPERATURE), "Temperatura"); return czujnikiInfo; }