Usługi Google Play i uprawnienia czasu działania

Od Androida 6.0 Marshmallow korzysta się z modelu uprawnień, który upraszcza proces instalowania aplikacji i automatycznych aktualizacji. Uprawnienia są wysyłane w czasie działania aplikacji, a nie przed jej zainstalowaniem. Użytkownicy mogą też odmówić przyznania pewnych uprawnień. Aby zapewnić użytkownikom taką elastyczność, musisz upewnić się, że aplikacja działa zgodnie z oczekiwaniami, gdy użytkownik włącza lub wyłącza określone uprawnienie.

Usługi Google Play mają uprawnienia czasu działania, które użytkownicy mogą wybrać niezależnie od uprawnień, o które prosi aplikacja. Usługi Google Play automatycznie uzyskują wszystkie uprawnienia potrzebne do obsługi interfejsów API. Aplikacja powinna jednak nadal sprawdzać i wysyłać prośby o uprawnienia w czasie działania aplikacji, a także odpowiednio obsługiwać błędy w przypadkach, gdy użytkownik odmówił przyznania Usługom Google Play uprawnień wymaganych do korzystania z interfejsu API przez aplikację.

Warto też radzić sobie z oczekiwaniami użytkownika dotyczącymi uprawnień, których może wymagać środowisko wykonawcze. Podane niżej sprawdzone metody pomogą Ci uniknąć potencjalnych problemów.

Wymagania wstępne

Uprawnienia musisz zadeklarować w pliku AndroidManifest.xml. Na przykład:

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

Wytyczne

Przed wywołaniem interfejsów API sprawdź uprawnienia

Po zadeklarowaniu interfejsów API, których chcesz używać w pliku AndroidManifest.xml, przed wywołaniem API upewnij się, że masz wymagane uprawnienia. Możesz to zrobić za pomocą metody checkSelfPermission w ActivityCompat lub ContextCompat.

Jeśli wywołanie zwróci wartość false (fałsz), oznacza to, że uprawnienia nie zostały przyznane i aby o nie poprosić, użyj polecenia requestPermissions. Odpowiedź na to pytanie jest zwracana w wywołaniu zwrotnym, które zobaczysz w kolejnym kroku.

Na przykład:

if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
    != PackageManager.PERMISSION_GRANTED) {
  // Check Permissions Now
  ActivityCompat.requestPermissions(this,
      new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
      REQUEST_LOCATION);
} else {
  // permission has been granted, continue as usual
  Task<Location> locationResult = LocationServices
      .getFusedLocationProviderClient(this /** Context */)
      .getLastLocation();
}

Implementowanie wywołania zwrotnego prośby o uprawnienie

Jeśli uprawnienia, których potrzebuje aplikacja, nie zostały przyznane przez użytkownika, należy wywołać metodę requestPermissions z prośbą o przyznanie ich. Odpowiedź użytkownika jest rejestrowana w wywołaniu zwrotnym onRequestPermissionsResult. Twoja aplikacja powinna go wdrożyć i zawsze sprawdzać zwracane wartości, ponieważ żądanie może zostać odrzucone lub anulowane. Możesz też poprosić o wiele uprawnień i sprawdzić je raz – poniższy przykład sprawdza tylko jedno uprawnienie.

public void onRequestPermissionsResult(int requestCode,
                                       String[] permissions,
                                       int[] grantResults) {
    if (requestCode == REQUEST_LOCATION) {
        if(grantResults.length == 1
           && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            // We can now safely use the API we requested access to
            Task<Location> locationResult = LocationServices
                .getFusedLocationProviderClient(this /** Context */)
                .getLastLocation();
        } else {
            // Permission was denied or request was cancelled
        }
    }
}

Pokaż uzasadnienie udzielenia zgody

Jeśli uprawnienia, których żąda aplikacja, są niezbędne do obsługi podstawowych funkcji aplikacji, a użytkownik wcześniej je odrzucił, aplikacja powinna wyświetlić dodatkowe wyjaśnienie, zanim ponownie poprosisz o niego. Użytkownicy chętniej przyznają uprawnienia, gdy wiedzą, do czego są potrzebne i jakie korzyści przyniosą im natychmiast.

W takim przypadku przed wywołaniem requestPermissions należy wywołać metodę shouldShowRequestPermissionRationale. Jeśli zwraca ono wartość „true” (prawda), należy utworzyć interfejs użytkownika, aby wyświetlić dodatkowy kontekst dla uprawnienia.

Kod może wyglądać np. tak:

if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
    != PackageManager.PERMISSION_GRANTED) {
    // Check Permissions Now
    private static final int REQUEST_LOCATION = 2;

    if (ActivityCompat.shouldShowRequestPermissionRationale(this,
            Manifest.permission.ACCESS_FINE_LOCATION)) {
        // Display UI and wait for user interaction
    } else {
        ActivityCompat.requestPermissions(
            this, new String[]{Manifest.permission.LOCATION_FINE},
            ACCESS_FINE_LOCATION);
    }
} else {
    // permission has been granted, continue as usual
    Task<Location> locationResult = LocationServices
        .getFusedLocationProviderClient(this /** Context */)
        .getLastLocation();
}

Obsługa błędów połączeń

Jeśli Twoja aplikacja używa wycofanej wersji GoogleApiClient, podczas wywołania metody connect() Usługi Google Play sprawdzają, czy ma ona wszystkie wymagane uprawnienia. connect() kończy się niepowodzeniem, gdy brakuje grup uprawnień wymaganych przez Usługi Google Play.

Jeśli wywołanie connect() nie powiedzie się, sprawdź, czy aplikacja prawidłowo obsługuje błąd połączenia. Jeśli Usługi Google Play nie mają uprawnień, możesz wywołać startResolutionForResult(), aby zainicjować przepływ użytkowników i rozwiązać te problemy.

Na przykład:

@Override
public void onConnectionFailed(ConnectionResult result) {
    if (mResolvingError) {
        // Already attempting to resolve an error.
        return;
    } else if (result.hasResolution()) {
        try {
            mResolvingError = true;
            result.startResolutionForResult(this, REQUEST_RESOLVE_ERROR);
        } catch (SendIntentException e) {
            // There was an error with the resolution intent. Try again.
            mGoogleApiClient.connect();
        }
    } else {
        // Show dialog using GooglePlayServicesUtil.getErrorDialog()
        showErrorDialog(result.getErrorCode());
        mResolvingError = true;
    }
}

Nowsze wywołania interfejsu API oparte na GoogleApi automatycznie wyświetlają okno (jeśli wystąpienie klienta jest tworzone przy użyciu Activity) lub powiadomienie w obszarze powiadomień (jeśli klient został utworzony przy użyciu Context), które użytkownik może kliknąć, aby rozpocząć intencję rozpoznawania uprawnień. Po przyznaniu uprawnień wywołania będą umieszczane w kolejce i ponawiane ponawiane.