Jak uruchomićForeground () bez pokazywania powiadomienia?


87

Chcę utworzyć usługę i uruchomić ją na pierwszym planie.

Większość przykładowych kodów zawiera powiadomienia. Ale nie chcę pokazywać żadnego powiadomienia. Czy to jest możliwe?

Czy możesz podać mi kilka przykładów? Czy są jakieś alternatywy?

Moja usługa aplikacji obsługuje odtwarzacz multimediów. Jak sprawić, by system nie zabijał mojej usługi, z wyjątkiem aplikacji, która sama ją zabija (np. Wstrzymywanie lub zatrzymywanie muzyki za pomocą przycisku).


11
Nie możesz mieć usługi „pierwszego planu” bez powiadomienia. Kropka.
Jug6ernaut

1
A co powiesz na „fałszywe” powiadomienie? to jest sztuczka?
Muhammad Resna Rizki Pratama

Po skonfigurowaniu powiadomienia na pierwszym planie możesz usunąć „powiadomienia działające w tle” za pomocą aplikacji Rubber .
Laurent

1
@MuhammadResnaRizkiPratama, czy rozważyłbyś zmianę zaakceptowanej odpowiedzi? To pytanie jest niezwykle popularne, ale przyjęta odpowiedź jest całkowicie nieaktualna, zbyt uciążliwa i zakłada, dlaczego deweloper tego potrzebuje. Proponuję tę odpowiedź, ponieważ jest niezawodna, nie wykorzystuje błędów systemu operacyjnego i działa na większości urządzeń z Androidem.
Sam

Odpowiedzi:


95

Ze względów bezpieczeństwa platformy Android nie można w żadnych okolicznościach mieć usługi pierwszoplanowej bez powiadomienia. Dzieje się tak, ponieważ usługa pierwszoplanowa zużywa większą ilość zasobów i podlega innym ograniczeniom związanym z planowaniem (tj. Nie zostaje tak szybko zabita) niż usługi działające w tle, a użytkownik musi wiedzieć, co prawdopodobnie zjada jego baterię. Więc nie rób tego rób tego.

Jednakże, to jest możliwe, aby mieć „fake” zawiadomienie, czyli można zrobić przezroczystą ikonę powiadomienia (IIRC). Jest to wyjątkowo nieszczere dla użytkowników i nie masz powodu, aby to robić, poza zabijaniem ich baterii i tworzeniem w ten sposób złośliwego oprogramowania.


3
Dzięki. To wyjaśnia, dlaczego setForeground potrzebuje powiadomienia.
Muhammad Resna Rizki Pratama

21
Wygląda na to, że masz powód, aby to zrobić, użytkownicy o to pytali. Chociaż może to być nieszczere, jak mówisz, grupy testowe poprosiły o to dla produktu, nad którym pracuję. Może Android powinien mieć opcję ukrywania powiadomień z niektórych usług, o których wiesz, że są zawsze uruchomione. W przeciwnym razie pasek powiadomień będzie wyglądał jak choinka.
Radu

3
@Radu właściwie nie powinieneś mieć tak wielu aplikacji na pierwszym planie: to zabije żywotność baterii, ponieważ są zaplanowane inaczej (zasadniczo nie umierają). Może to być akceptowalne w przypadku modyfikacji, ale nie widzę, aby ta opcja w najbliższym czasie stała się waniliowym Androidem. „Użytkownicy” zwykle nie wiedzą, co jest dla nich najlepsze ..
Kristopher Micinski

2
@Radu, który nie obsługuje twojego argumentu. „Nagradzane aplikacje” to zazwyczaj te, które faktycznie „naprawiają” dziury w systemie Android poprzez niespójności systemowe (np. Zabijacze zadań). Nie ma powodu, abyś musiał korzystać z usługi pierwszego planu tylko dlatego, że tak robią „nagradzane aplikacje”: jeśli to robisz, przyznajesz się, że zabijasz baterię użytkownika.
Kristopher Micinski

2
Najwyraźniej ta metoda nie będzie już działać od wersji 4.3. plus.google.com/u/0/105051985738280261832/posts/MTinJWdNL8t
erbi

79

Aktualizacja: problem został „naprawiony” w systemie Android 7.1. https://code.google.com/p/android/issues/detail?id=213309

Od aktualizacji 4.3 praktycznie niemożliwe jest uruchomienie usługi startForeground()bez wyświetlania powiadomienia.

Możesz jednak ukryć ikonę za pomocą oficjalnych interfejsów API ... przezroczysta ikona nie jest potrzebna: (Użyj NotificationCompatdo obsługi starszych wersji)

NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
builder.setPriority(Notification.PRIORITY_MIN);

Pogodziłem się z faktem, że samo powiadomienie nadal musi tam być, ale dla każdego, kto nadal chce je ukryć, być może znalazłem również obejście tego problemu:

  1. Rozpocznij fałszywą usługę z startForeground()powiadomieniem i wszystkim.
  2. Uruchom prawdziwą usługę, którą chcesz uruchomić, również z startForeground()(tym samym identyfikatorem powiadomienia)
  3. Zatrzymaj pierwszą (fałszywą) usługę (możesz zadzwonić stopSelf()i zadzwonić do połączenia onDestroy stopForeground(true)).

Voilà! Brak powiadomienia, a druga usługa nadal działa.


23
Dzięki za to. Myślę, że niektórzy ludzie nie zdają sobie sprawy z tego, że istnieje rozwój Androida, który jest przeznaczony do wewnętrznego użytku biznesowego (to znaczy, że nigdy nie pojawił się na rynku ani nie został sprzedany Joe Blowowi). Czasami ludzie proszą o takie rzeczy, jak „przestań pokazywać powiadomienie o usłudze” i dobrze jest zobaczyć obejścia, nawet jeśli nie są one koszerne do ogólnego spożycia.
JamieB

5
@JamieB Nie mogłem się bardziej zgodzić ... Zasugerowałem Dianne Hackborn, że muszą przemyśleć całą koncepcję usług w tle i być może dodać pozwolenie na uruchamianie usługi w tle bez powiadomień. Dla nas, programistów, nie ma znaczenia, czy jest powiadomienie, czy nie - to prośba UŻYTKOWNIKA, aby je usunąć! :) Swoją drogą, czy możesz sprawdzić, czy to działa również u Ciebie?
Lior Iluz

1
@JamieB user875707 mówi, aby ustawić parametr ikony (identyfikator zasobu ikony) na 0, a nie identyfikator powiadomienia (jeśli tak, zgodnie z dokumentacją „startForeground” nie zadziała).
wangqi060934

9
Powinieneś oczywiście założyć, że to nie zadziała w przyszłych wersjach platformy.
hackbod

1
@JamieB Testowałem to teraz na Androidzie 5.0.1 i nadal działa dla mnie.
Lior Iluz

20

To już nie działa od Androida 7.1 i może naruszać zasady dla programistów Google Play .

Zamiast tego poproś użytkownika o zablokowanie powiadomienia o usłudze .


Oto mój Wdrożenie techniki w odpowiedzi przez Lior Iluz .

Kod

ForegroundService.java

public class ForegroundService extends Service {

    static ForegroundService instance;

    @Override
    public void onCreate() {
        super.onCreate();

        instance = this;

        if (startService(new Intent(this, ForegroundEnablingService.class)) == null)
            throw new RuntimeException("Couldn't find " + ForegroundEnablingService.class.getSimpleName());
    }

    @Override
    public void onDestroy() {
        super.onDestroy();

        instance = null;
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

}

ForegroundEnablingService.java

public class ForegroundEnablingService extends Service {

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (ForegroundService.instance == null)
            throw new RuntimeException(ForegroundService.class.getSimpleName() + " not running");

        //Set both services to foreground using the same notification id, resulting in just one notification
        startForeground(ForegroundService.instance);
        startForeground(this);

        //Cancel this service's notification, resulting in zero notifications
        stopForeground(true);

        //Stop this service so we don't waste RAM.
        //Must only be called *after* doing the work or the notification won't be hidden.
        stopSelf();

        return START_NOT_STICKY;
    }

    private static final int NOTIFICATION_ID = 10;

    private static void startForeground(Service service) {
        Notification notification = new Notification.Builder(service).getNotification();
        service.startForeground(NOTIFICATION_ID, notification);
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

}

AndroidManifest.xml

<service android:name=".ForegroundEnablingService" />
<service android:name=".ForegroundService" />

Zgodność

Przetestowane i pracuję nad:

  • Oficjalny emulator
    • 4.0.2
    • 4.1.2
    • 4.2.2
    • 4.3.1
    • 4.4.2
    • 5.0.2
    • 5.1.1
    • 6.0
    • 7.0
  • Sony Xperia M.
    • 4.1.2
    • 4.3
  • Samsung Galaxy?
    • 4.4.2
    • 5.X
  • Genymotion
    • 5.0
    • 6.0
  • CyanogenMod
    • 5.1.1

Nie działa już od Androida 7.1.


2
Jeszcze tego nie testowałem, ale byłoby miło mieć przynajmniej na to pozwolenie !!! +1 dla części „Do Google”. Moi użytkownicy narzekają na powiadomienie wymagane, aby aplikacja działała w tle.
Narzekam

przepraszam, jak można wykorzystać ten przykład do nasłuchiwania alarmów. Im się tworzeniem aplikacji, które wyróżniają alarmy z alarmManager i problem mam to, że kiedy zamknę app (Najnowsze apps-> Clear) wszystkie alarmy zostaną usunięte, jak również i im traing znaleźć sposób, aby temu zapobiec
Ares91

@ Ares91, spróbuj stworzyć osobne pytanie dotyczące StackOverflow i upewnij się, że zawiera jak najwięcej informacji, w tym odpowiedni kod. Niewiele wiem o alarmach, a to pytanie dotyczy tylko usług.
Sam

@Sam, która usługa powinna zostać wywołana jako pierwsza, ForegroundEnablingService lub Forground
Android Man

@AndroidManForegroundService
Sam

14

Możesz tego użyć (zgodnie z sugestią @Kristopher Micinski):

Notification note = new Notification( 0, null, System.currentTimeMillis() );
note.flags |= Notification.FLAG_NO_CLEAR;
startForeground( 42, note );

AKTUALIZACJA:

Należy pamiętać, że nie jest to już dozwolone w wersjach Androida KitKat +. I pamiętaj, że jest to mniej więcej naruszenie zasady projektowania w Androidzie, która sprawia, że ​​operacje w tle są widoczne dla użytkowników, jak wspomniał @Kristopher Micinski


2
Zauważ, że wydaje się, że nie jest to już akceptowane w SDK 17. Usługa nie przejdzie na pierwszy plan, jeśli element do wyciągnięcia przekazany do powiadomienia to 0.
Snicolas

To był błąd i miejmy nadzieję, że został naprawiony. Ideą usług pierwszego planu jest to, że są one mniej podatne na zabicie i jest to sposób na upewnienie się, że użytkownik jest świadomy ich istnienia.
Martin Marconcini

4
W Androidzie 18 powiadomienie nie jest już niewidoczne, ale wyświetla komunikat „Aplikacja działa, dotknij, aby uzyskać więcej informacji lub zatrzymać aplikację”
Emanuel Moecklin

1
Zrzut ekranu jednego z moich klientów: 1gravity.com/k10/Screenshot_2013-07-25-12-35-49.jpg . Mam też urządzenie 4.3 i mogę to potwierdzić.
Emanuel Moecklin

2
Ostrzeżenie: otrzymuję raporty o awariach od Sony Ericsson LT26i (Xperia S) z systemem Android 4.1.2 (SDK 16): android.app.RemoteServiceException: Złe powiadomienie wysłane z pakietu ...: Nie można utworzyć ikony: StatusBarIcon (pkg = ... id = 0x7f020052 poziom = 0 widoczny = prawda num = 0) Ustawiłem identyfikator ikony na 0 do SDK 17 również. Z SDK 18 ustawiłem go na prawidłowy zasób do rysowania. Może potrzebujesz prawidłowego identyfikatora ikony z SDK 16!
almisoft,

13

Ostrzeżenie : chociaż wydaje się, że ta odpowiedź działa, w rzeczywistości po cichu zapobiega to, aby Twoja usługa stała się usługą pierwszego planu .

Oryginalna odpowiedź:


Po prostu ustaw identyfikator powiadomienia na zero:

// field for notification ID
private static final int NOTIF_ID = 0;

    ...
    startForeground(NOTIF_ID, mBuilder.build());
    NotificationManager mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
    mNotificationManager.cancel(NOTIF_ID);
    ...

Korzyści, jakie możesz uzyskać, to: a Service będzie w stanie działać z wysokim priorytetem bez niszczenia przez system Android, chyba że przy dużym obciążeniu pamięci.

EDYTOWAĆ

Aby działał z wersją Pre-Honeycomb i Androidem 4.4 i nowszymi wersjami, upewnij się, że używasz tego, NotificationCompat.Builderco zapewnia Support Library v7, a nie Notification.Builder.


1
To działa idealnie dla mnie na 4.3 (18) @vlad sol. Dlaczego nie ma jeszcze innych głosów za tą odpowiedzią, zastanawiam się?
Rob McFeely,

Wypróbowałem to w Android N Preview 4 i nie zadziałało. Próbowałem Notification.Builder, Support Library v4 NotificationCompat.Builderi Support Library v7 NotificationCompat.Builder. Powiadomienie nie pojawiło się w szufladzie powiadomień ani na pasku stanu, ale usługa nie działała w trybie pierwszego planu, gdy sprawdzałem ją przy użyciu getRunningServices()i dumpsys.
Sam

@Sam, więc ten błąd nie pojawia się we wcześniejszej wersji, prawda?
Anggrayudi H

@AnggrayudiH, przez „ten błąd”, masz na myśli, że powiadomienie się nie pojawia? Czy masz na myśli, że usługa nie przechodzi w tryb pierwszoplanowy?
Sam

1
To nie działa dla mnie. Proces jest uruchamiany jako pierwszy plan, gdy wysyłane jest id! = 0, ale NIE rozpoczyna się jako pierwszy plan, gdy id = 0. Użyj adb shell dumpsys activity servicesdo weryfikacji.
burak cukrowy

8

Możesz ukryć powiadomienia w Androidzie 9+ , używając niestandardowego układu z layout_height = "0dp"

NotificationCompat.Builder builder = new NotificationCompat.Builder(context, NotificationUtils.CHANNEL_ID);
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.custom_notif);
builder.setContent(remoteViews);
builder.setPriority(NotificationCompat.PRIORITY_LOW);
builder.setVisibility(Notification.VISIBILITY_SECRET);

custom_notif.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="0dp">
</LinearLayout>

Testowane na Pixelu 1, Androidzie 9. To rozwiązanie nie działa na Androidzie 8 lub starszym


Niezłe obejście. Myślę też, że wymaga ikony na Androidzie 10. Użycie przezroczystego obrazu zastępczego dla ikony rozwiązuje problem.
Shyos

7

Aktualizacja: to już nie działa w systemie Android 4.3 i nowszych


Istnieje jedno obejście. Spróbuj utworzyć powiadomienie bez ikony ustawień, a powiadomienie się nie pojawi. Nie wiem, jak to działa, ale działa :)

    Notification notification = new NotificationCompat.Builder(this)
            .setContentTitle("Title")
            .setTicker("Title")
            .setContentText("App running")
            //.setSmallIcon(R.drawable.picture)
            .build();
    startForeground(101,  notification);

3
W Android N Preview 4 to nie działa. Android wyświetla (YourServiceName) is runningpowiadomienie zamiast własnego, jeśli nie określisz ikony. Według CommonsWare dzieje się tak od czasu Androida 4.3: commonsware.com/blog/2013/07/30/…
Sam,

1
Zrobiłem dzisiaj trochę testów i potwierdziłem, że to nie działa na Androidzie 4.3 i nowszych.
Sam

5

Aktualizacja: to już nie działa w systemie Android 4.3 i nowszych


Ustawiłem parametr icon na konstruktor dla Notification na zero, a następnie przekazałem wynikowe powiadomienie do startForeground (). Brak błędów w dzienniku i brak powiadomień. Nie wiem jednak, czy usługa została pomyślnie uruchomiona - czy można to sprawdzić?

Edytowane: sprawdzone za pomocą dumpsys i rzeczywiście usługa jest na pierwszym planie w moim systemie 2.3. Nie sprawdzałem jeszcze z innymi wersjami systemu operacyjnego.


9
Użyj polecenia „adb shell dumpsys activity services”, aby sprawdzić, czy „isForeground” ma wartość true.
czarny

1
Lub po prostu wywołaj pusty Notification()konstruktor.
johsin18

To zadziałało dla mnie. Pierwszy raz uruchomiłem usługę w nocy, więc jest zdecydowanie na pierwszym planie (plus dumpsys mówi, że tak) i nie ma powiadomienia.
JamieB

Zauważ, że to już nie działa w systemie Android 4.3 (API 18). System operacyjny umieszcza powiadomienie zastępcze w szufladzie powiadomień.
Sam

4

Zablokuj powiadomienie usługi na pierwszym planie

Większość odpowiedzi tutaj nie działa, przerywa działanie usługi pierwszego planu lub narusza zasady Google Play .

Jedynym sposobem na niezawodne i bezpieczne ukrycie powiadomienia jest zablokowanie go przez użytkownika.

Android 4.1 - 7.1

Jedynym sposobem jest zablokowanie wszystkich powiadomień z Twojej aplikacji:

  1. Wyślij użytkownika do ekranu szczegółów aplikacji:

    Uri uri = Uri.fromParts("package", getPackageName(), null);
    Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).setData(uri);
    startActivity(intent);
    
  2. Miej powiadomienia aplikacji o blokowaniu użytkowników

Pamiętaj, że blokuje to również tosty Twojej aplikacji.

Android 8.0 - 8.1

Nie warto blokować powiadomienia na Androidzie O, ponieważ system operacyjny po prostu zastąpi je „ działającym w tle ” lub „ korzystającym z baterii ”.

Android 9+

Użyj kanału powiadomień, aby zablokować powiadomienie o usłudze bez wpływu na inne powiadomienia.

  1. Przypisz powiadomienie o usłudze do kanału powiadomień
  2. Wyślij użytkownika do ustawień kanału powiadomień

    Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
        .putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName())
        .putExtra(Settings.EXTRA_CHANNEL_ID, myNotificationChannel.getId());
    startActivity(intent);
    
  3. Miej powiadomienia o blokowaniu użytkowników przez kanał


2

w wersji 4.3 (18) i nowszych ukrywanie powiadomienia serwisowego nie jest możliwe, ale można wyłączyć ikonę, w wersji 4.3 (18) i niższych można ukryć powiadomienie

Notification noti = new Notification();
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN) {
    noti.priority = Notification.PRIORITY_MIN;
}
startForeground(R.string.app_name, noti);

Chociaż ta odpowiedź jest prawdopodobnie poprawna i przydatna, najlepiej jest dołączyć do niej jakieś wyjaśnienie, aby wyjaśnić, w jaki sposób pomaga ona rozwiązać problem. Staje się to szczególnie przydatne w przyszłości, jeśli nastąpi zmiana (prawdopodobnie niezwiązana), która spowoduje, że przestanie działać, a użytkownicy muszą zrozumieć, jak kiedyś działało.
Kevin Brown

Myślę, że Android 4.3 to faktycznie API 18. Ponadto ta metoda tylko ukrywa ikonę na pasku stanu; ikona nadal istnieje w powiadomieniu.
Sam,

do ukrycia ikony z powiadomienia można dodać pusty obraz mBuilder.setSmallIcon (R.drawable.blank);
vlad sol

1
Patrzę na źródło KitKat, a metoda zerowego identyfikatora nie wydaje się działać. jeśli identyfikator jest równy zero, rekord usługi jest aktualizowany do NIE pierwszego planu.
Jeffrey Blattman

2

Odkryłem, że na Androidzie 8.0 jest to nadal możliwe, nie używając kanału powiadomień.

public class BootCompletedIntentReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if ("android.intent.action.BOOT_COMPLETED".equals(intent.getAction())) {

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {

                Intent notificationIntent = new Intent(context, BluetoothService.class);    
                context.startForegroundService(notificationIntent);

            } else {
                //...
            }

        }
    }
}

A w BluetoothService.class:

 @Override
    public void onCreate(){    
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {

            Intent notificationIntent = new Intent(this, BluetoothService.class);

            PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);

            Notification notification = new Notification.Builder(this)
                    .setContentTitle("Title")
                    .setContentText("App is running")
                    .setSmallIcon(R.drawable.notif)
                    .setContentIntent(pendingIntent)
                    .setTicker("Title")
                    .setPriority(Notification.PRIORITY_DEFAULT)
                    .build();

            startForeground(15, notification);

        }

    }

Trwałe powiadomienie nie jest wyświetlane, ale zobaczysz powiadomienie na Androida „x aplikacje działają w tle”.


W systemie Android 8.1: „Złe powiadomienie dla startForeground: java.lang.RuntimeException: nieprawidłowy kanał dla powiadomienia o usłudze”
T-igra,

Osobiście uważam, że apps are running in the backgroundpowiadomienie jest jeszcze gorsze niż powiadomienie specyficzne dla aplikacji, więc nie jestem pewien, czy takie podejście jest tego warte.
Sam,

-2

Aktualizacja: to już nie działa w systemie Android 7.1 i nowszych


Oto sposób na ustawienie oom_adj aplikacji na 1 (testowane w emulatorze ANDROID 6.0 SDK).Dodaj usługę tymczasową do głównego zgłoszenia serwisowego startForgroundService(NOTIFICATION_ID, notificion). Następnie ponownie uruchom tymczasowe startForgroundService(NOTIFICATION_ID, notificion)zgłoszenie serwisowe z tym samym identyfikatorem powiadomienia, po chwili w tymczasowym wywołaniu serwisowym stopForgroundService (true), aby odrzucić ontyfikację.


To jedyne działające rozwiązanie, o którym wiem. Jednak jest już objęta inną odpowiedzią .
Sam

-3

Możesz również zadeklarować swoją aplikację jako trwałą.

<application
    android:icon="@drawable/icon"
    android:label="@string/app_name"
    android:theme="@style/Theme"
    *android:persistent="true"* >
</application>

To zasadniczo ustawia twoją aplikację na wyższy priorytet pamięci, zmniejszając prawdopodobieństwo jej zabicia.


5
To jest złe, tylko aplikacje systemowe mogą być trwałe: groups.google.com/forum/?fromgroups=#!topic/android-developers/…
Snicolas

-6

Kilka miesięcy temu opracowałem prosty odtwarzacz multimedialny. Więc wierzę, że robisz coś takiego:

Intent i = new Intent(this, someServiceclass.class);

startService (i);

Wtedy system nie powinien być w stanie zabić twojej usługi.

odniesienie : przeczytaj akapit, w którym omówiono, kiedy system zatrzymuje usługę


Czytałem w niektórych artykułach. Jeśli po prostu uruchomisz startService (), system potrzebuje więcej pamięci. Więc system zabije tę usługę. to prawda? Po prostu nie mam czasu na eksperymentowanie w służbie.
Muhammad Resna Rizki Pratama

1
Muhammad, to prawda - celem startForeground () jest efektywne nadanie usłudze wyższego „priorytetu pamięci” niż usługom działającym w tle, aby mogła ona przetrwać dłużej. Metoda startForeground () powinna używać powiadomienia, aby użytkownik był bardziej świadomy działającej usługi, która jest bardziej trwała / trudniejsza do zabicia.
DustinB

To po prostu nieprawda; stworzenie usługi, której nie można zabić na Androidzie jest praktycznie niemożliwe. Android z łatwością zabija usługi nie pierwszoplanowe, aby zwolnić pamięć.
Sam
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.