גישה ל-Google APIs

כדי לשלוח קריאה לאחד מממשקי ה-API בערכת SDK שמופעלת על ידי Google Play Services, כמו כניסה באמצעות חשבון Google או ML Kit, אתם צריכים קודם ליצור מכונה של אובייקט לקוח API. האובייקטים האלו מנהלים באופן אוטומטי את החיבור אל Google Play Services. כשיש חיבור זמין, כל אובייקט לקוח ב-API מפעיל בקשות לפי הסדר. אחרת, אובייקט הלקוח מוסיף את הבקשות לתור. אם לא צוין אחרת, המבנה של אובייקטים של לקוח זול. אין בעיה ליצור לקוחות API חדשים בכל פעם שתרצו להפעיל methods של API.

במדריך הזה מוסבר איך לשלוח קריאות ל-API לכל ערכות SDK שמבוססות על Google Play Services, כולל איך לגשת לשירותים שלא מחייבים הרשאה ולשירותים שדורשים הרשאה.

שנתחיל?

כדי להתחיל, מוסיפים את הכלים ואת יחסי התלות הנדרשים בפרויקט האפליקציה, כפי שמתואר במדריך להגדרת Google Play Services.

גישה כשלא נדרשת הרשאה

כדי לגשת לשירות שלא דורש הרשאה ב-API, מבקשים מופע של אובייקט הלקוח של השירות ומעבירים אותו באמצעות ה-Context הנוכחי או ה-Activity הנוכחי. לפני הרצת קריאות ל-API, המשתמשים יתבקשו לשדרג את שירותי Google Play במידת הצורך.

לדוגמה, כדי לקבל את המיקום הידוע האחרון של המכשיר באמצעות Fused Location Provider ל-Android, מוסיפים את הלוגיקה שמוצגת בקטע הקוד הבא:

Kotlin

// Code required for requesting location permissions omitted for brevity.
val client = LocationServices.getFusedLocationProviderClient(this)

// Get the last known location. In some rare situations, this can be null.
client.lastLocation.addOnSuccessListener { location : Location? ->
    location?.let {
        // Logic to handle location object.
    }
}

Java

// Code required for requesting location permissions omitted for brevity.
FusedLocationProviderClient client =
        LocationServices.getFusedLocationProviderClient(this);

// Get the last known location. In some rare situations, this can be null.
client.getLastLocation()
        .addOnSuccessListener(this, location -> {
            if (location != null) {
                // Logic to handle location object.
            }
        });

גישה כשנדרשת הרשאה

כדי לגשת לשירות שמחייב הרשאת משתמש, מבצעים את הפעולות הבאות:

  1. מזינים את המשתמש לחשבון.
  2. מבקשים הרשאת גישה להיקפי ההרשאות שנדרשים לשירות.
  3. מקבלים מופע של אובייקט הלקוח של השירות ומעבירים אותו לאובייקט GoogleSignInAccount של המשתמש, בנוסף לאובייקט Context או Activity.

בדוגמה הבאה אנחנו מטמיעים את קריאת השלבים היומיים של המשתמש באמצעות Google Fit API. כדי לצפות בהטמעה דומה בהקשר של פרויקט מלא, כדאי לצפות בפעילות העיקרית של אפליקציית BasicHistoryApiKotlin ב-GitHub.

Kotlin

class FitFragment : Fragment() {
    private val fitnessOptions: FitnessOptions by lazy {
        FitnessOptions.builder()
            .addDataType(DataType.TYPE_STEP_COUNT_CUMULATIVE)
            .addDataType(DataType.TYPE_STEP_COUNT_DELTA)
            .build()
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        fitSignIn()
    }

    /*
     * Checks whether the user is signed in. If so, executes the specified
     * function. If the user is not signed in, initiates the sign-in flow,
     * specifying the function to execute after the user signs in.
     */
    private fun fitSignIn() {
        if (oAuthPermissionsApproved()) {
            readDailySteps()
        } else {
            GoogleSignIn.requestPermissions(
                this,
                SIGN_IN_REQUEST_CODE,
                getGoogleAccount(),
                fitnessOptions
            )
        }
    }

    private fun oAuthPermissionsApproved() =
        GoogleSignIn.hasPermissions(getGoogleAccount(), fitnessOptions)

    /*
     * Gets a Google account for use in creating the fitness client. This is
     * achieved by either using the last signed-in account, or if necessary,
     * prompting the user to sign in. It's better to use the
     * getAccountForExtension() method instead of the getLastSignedInAccount()
     * method because the latter can return null if there has been no sign in
     * before.
     */
    private fun getGoogleAccount(): GoogleSignInAccount =
        GoogleSignIn.getAccountForExtension(requireContext(), fitnessOptions)

    /*
     * Handles the callback from the OAuth sign in flow, executing the function
     * after sign-in is complete.
     */
    override fun onActivityResult(
            requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        when (resultCode) {
            RESULT_OK -> {
                readDailySteps()
            }
            else -> {
                // Handle error.
            }
        }
    }

    /*
     * Reads the current daily step total.
     */
    private fun readDailySteps() {
        Fitness.getHistoryClient(requireContext(), getGoogleAccount())
            .readDailyTotal(DataType.TYPE_STEP_COUNT_DELTA)
            .addOnSuccessListener { dataSet ->
                val total = when {
                    dataSet.isEmpty -> 0
                    else -> dataSet.dataPoints.first()
                            .getValue(Field.FIELD_STEPS).asInt()
                }

                Log.i(TAG, "Total steps: $total")
            }
            .addOnFailureListener { e ->
                Log.w(TAG, "There was a problem getting the step count.", e)
            }
    }

    companion object {
        const val SIGN_IN_REQUEST_CODE = 1001
    }
}

Java

public class FitFragment extends Fragment {
    private final FitnessOptions fitnessOptions = FitnessOptions.builder()
            .addDataType(DataType.TYPE_STEP_COUNT_CUMULATIVE)
            .addDataType(DataType.TYPE_STEP_COUNT_DELTA)
            .build();

    @Override
    public void onViewCreated(
            @NotNull View view, @Nullable Bundle savedInstanceState) {
        fitSignIn();
    }

    /*
     * Checks whether the user is signed in. If so, executes the specified
     * function. If the user is not signed in, initiates the sign-in flow,
     * specifying the function to execute after the user signs in.
     */
    private void fitSignIn() {
        if (oAuthPermissionsApproved()) {
            readDailySteps();
        } else {
            GoogleSignIn.requestPermissions(this, SIGN_IN_REQUEST_CODE,
                    getGoogleAccount(), fitnessOptions);
        }
    }

    private boolean oAuthPermissionsApproved() {
        return GoogleSignIn.hasPermissions(getGoogleAccount(), fitnessOptions);
    }

    /*
     * Gets a Google account for use in creating the fitness client. This is
     * achieved by either using the last signed-in account, or if necessary,
     * prompting the user to sign in. It's better to use the
     * getAccountForExtension() method instead of the getLastSignedInAccount()
     * method because the latter can return null if there has been no sign in
     * before.
     */
    private GoogleSignInAccount getGoogleAccount() {
        return GoogleSignIn.getAccountForExtension(
                requireContext(), fitnessOptions);
    }

    /*
     * Handles the callback from the OAuth sign in flow, executing the function
     * after sign-in is complete.
     */
    @Override
    public void onActivityResult(
            int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode == RESULT_OK) {
            readDailySteps();
        } else {
            // Handle error.
        }
    }

    /*
     * Reads the current daily step total.
     */
    private void readDailySteps() {
        AtomicInteger total = new AtomicInteger();
        Fitness.getHistoryClient(requireContext(), getGoogleAccount())
                .readDailyTotal(DataType.TYPE_STEP_COUNT_DELTA)
                .addOnSuccessListener(dataSet -> {
                    if (!dataSet.isEmpty())
                        total.set(Integer.parseInt(dataSet.getDataPoints()
                                .get(0).getValue(FIELD_STEPS).toString()));
                        Log.i(TAG, "Total steps: $total");
                })
                .addOnFailureListener(e -> {
                    Log.w(TAG, "There was a problem getting the step count.", e);
                });
    }

    private static final int SIGN_IN_REQUEST_CODE = 1001;
}

בדיקת הזמינות של ה-API

לפני שמפעילים באפליקציה תכונה שתלויה ב-API של Google Play Services, צריך לבדוק את הזמינות של ה-API במכשיר. כדי לעשות את זה, צריך לבצע קריאה ל-checkApiAvailability().

קטע הקוד הבא מדגים איך לבדוק את הזמינות של ספק המיקום המשולב.

Kotlin

fun getLastLocationIfApiAvailable(context: Context?): Task<Location>? {
    val client = getFusedLocationProviderClient(context)
    return GoogleApiAvailability.getInstance()
        .checkApiAvailability(client)
        .onSuccessTask { _ -> client.lastLocation }
        .addOnFailureListener { _ -> Log.d(TAG, "Location unavailable.")}
}

Java

public Task<Location> getLastLocationIfApiAvailable(Context context) {
    FusedLocationProviderClient client =
            getFusedLocationProviderClient(context);
    return GoogleApiAvailability.getInstance()
            .checkApiAvailability(client)
            .onSuccessTask(unused -> client.getLastLocation())
            .addOnFailureListener(e -> Log.d(TAG, "Location unavailable."));
}