public final class

ComplicationHelperActivity

extends Activity

implements ActivityCompat.OnRequestPermissionsResultCallback

 java.lang.Object

↳Activity

↳androidx.wear.complications.ComplicationHelperActivity

Overview

Activity to handle permission requests for complications.

This can be used to start the provider chooser, making a permission request if necessary, or to just make a permission request, and update all active complications if the permission is granted.

To use, add this activity to your app, and also add the com.google.android.wearable.permission.RECEIVE_COMPLICATION_DATA permission.

Then, to start the provider chooser, use ComplicationHelperActivity.createProviderChooserHelperIntent(Context, ComponentName, int, int[]) to obtain an intent. If the permission has not yet been granted, the permission will be requested and the provider chooser will only be started if the request is accepted by the user.

Or, to request the permission, for instance if android.support.wearable.complications.ComplicationData of TYPE_NO_PERMISSION has been received and tapped on, use ComplicationHelperActivity.createPermissionRequestHelperIntent(Context, ComponentName).

Summary

Fields
public static final java.lang.StringACTION_PERMISSION_REQUEST_ONLY

public static final java.lang.StringACTION_REQUEST_UPDATE_ALL_ACTIVE

public static final java.lang.StringACTION_START_PROVIDER_CHOOSER

public static final java.lang.StringEXTRA_WATCH_FACE_COMPONENT

Constructors
publicComplicationHelperActivity()

Methods
public static IntentcreatePermissionRequestHelperIntent(Context context, ComponentName watchFace)

Returns an intent that may be used to start this activity in order to request the permission required to receive complication data.

public static IntentcreateProviderChooserHelperIntent(Context context, ComponentName watchFace, int watchFaceComplicationId, int[] supportedTypes[])

Returns an intent that may be used to start the provider chooser activity via the ComplicationHelperActivity.

protected voidonActivityResult(int requestCode, int resultCode, Intent data)

protected voidonCreate(Bundle savedInstanceState)

public voidonRequestPermissionsResult(int requestCode, java.lang.String permissions[], int[] grantResults[])

from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Fields

public static final java.lang.String ACTION_REQUEST_UPDATE_ALL_ACTIVE

public static final java.lang.String EXTRA_WATCH_FACE_COMPONENT

public static final java.lang.String ACTION_START_PROVIDER_CHOOSER

public static final java.lang.String ACTION_PERMISSION_REQUEST_ONLY

Constructors

public ComplicationHelperActivity()

Methods

protected void onCreate(Bundle savedInstanceState)

public void onRequestPermissionsResult(int requestCode, java.lang.String permissions[], int[] grantResults[])

protected void onActivityResult(int requestCode, int resultCode, Intent data)

public static Intent createProviderChooserHelperIntent(Context context, ComponentName watchFace, int watchFaceComplicationId, int[] supportedTypes[])

Returns an intent that may be used to start the provider chooser activity via the ComplicationHelperActivity. This allows the required permission to be checked before the provider chooser is displayed.

To use this, the ComplicationHelperActivity must be added to your app, and your app must include the com.google.android.wearable.permission.RECEIVE_COMPLICATION_DATA permission in its manifest.

The provider chooser activity will show a list of all providers that can supply data of at least one of the supportedTypes.

When the user chooses a provider, the configuration will be set up in the complications system - the watch face does not need to do anything else.

The activity may be started using . The result delivered back to your activity will have a result code of if a provider was successfully set, or a result code of if no provider was set. In the case where a provider was set, android.support.wearable.complications.ComplicationProviderInfo for the chosen provider will be included in the data intent of the result, as an extra with the key android.support.wearable.complications.EXTRA_PROVIDER_INFO.

The package of the calling app must match the package of the watch face, or this will not work.

Parameters:

context: context for the current app, that must contain a ComplicationHelperActivity
watchFace: the ComponentName of the WatchFaceService being configured.
watchFaceComplicationId: the watch face's id for the complication being configured. This must match the id passed in when the watch face calls WatchFaceService.Engine#setActiveComplications.
supportedTypes: the types supported by the complication, in decreasing order of preference. If a provider can supply data for more than one of these types, the type chosen will be whichever was specified first.

public static Intent createPermissionRequestHelperIntent(Context context, ComponentName watchFace)

Returns an intent that may be used to start this activity in order to request the permission required to receive complication data.

To use this, the ComplicationHelperActivity must be added to your app, and your app must include the com.google.android.wearable.permission.RECEIVE_COMPLICATION_DATA permission in its manifest.

If the current app has already been granted this permission, the activity will finish immediately.

If the current app has not been granted this permission, a permission request will be made. If the permission is granted by the user, an update of all complications on the current watch face will be triggered. The provided watchFace must match the current watch face for this to occur.

Parameters:

context: context for the current app, that must contain a ComplicationHelperActivity
watchFace: the ComponentName of the WatchFaceService for the current watch face

Source

/*
 * Copyright 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package androidx.wear.complications;

import android.annotation.TargetApi;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.support.wearable.complications.ComplicationData;
import android.support.wearable.complications.ComplicationProviderInfo;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.core.app.ActivityCompat;

import java.util.Objects;

/**
 * Activity to handle permission requests for complications.
 *
 * <p>This can be used to start the provider chooser, making a permission request if necessary, or
 * to just make a permission request, and update all active complications if the permission is
 * granted.
 *
 * <p>To use, add this activity to your app, and also add the {@code
 * com.google.android.wearable.permission.RECEIVE_COMPLICATION_DATA} permission.
 *
 * <p>Then, to start the provider chooser, use {@link #createProviderChooserHelperIntent} to obtain
 * an intent. If the permission has not yet been granted, the permission will be requested and the
 * provider chooser will only be started if the request is accepted by the user.
 *
 * <p>Or, to request the permission, for instance if {@link ComplicationData} of {@link
 * ComplicationData#TYPE_NO_PERMISSION TYPE_NO_PERMISSION} has been received and tapped on, use
 * {@link #createPermissionRequestHelperIntent}.
 */
@TargetApi(Build.VERSION_CODES.N)
@SuppressWarnings("ForbiddenSuperClass")
public final class ComplicationHelperActivity extends Activity
        implements ActivityCompat.OnRequestPermissionsResultCallback {

    /** @hide */
    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    public static final String ACTION_REQUEST_UPDATE_ALL_ACTIVE =
            "android.support.wearable.complications.ACTION_REQUEST_UPDATE_ALL_ACTIVE";

    /** @hide */
    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    public static final String EXTRA_WATCH_FACE_COMPONENT =
            "android.support.wearable.complications.EXTRA_WATCH_FACE_COMPONENT";

    /** @hide */
    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    public static final String ACTION_START_PROVIDER_CHOOSER =
            "android.support.wearable.complications.ACTION_START_PROVIDER_CHOOSER";

    /** @hide */
    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    public static final String ACTION_PERMISSION_REQUEST_ONLY =
            "android.support.wearable.complications.ACTION_PERMISSION_REQUEST_ONLY";

    /** The package of the service that accepts provider requests. */
    private static final String UPDATE_REQUEST_RECEIVER_PACKAGE = "com.google.android.wearable.app";

    private static final int START_REQUEST_CODE_PROVIDER_CHOOSER = 1;
    private static final int PERMISSION_REQUEST_CODE_PROVIDER_CHOOSER = 1;
    private static final int PERMISSION_REQUEST_CODE_REQUEST_ONLY = 2;

    private static final String COMPLICATIONS_PERMISSION =
            "com.google.android.wearable.permission.RECEIVE_COMPLICATION_DATA";

    private static final String COMPLICATIONS_PERMISSION_PRIVILEGED =
            "com.google.android.wearable.permission.RECEIVE_COMPLICATION_DATA_PRIVILEGED";

    @Nullable private ComponentName mWatchFace;
    private int mWfComplicationId;
    @Nullable @ComplicationData.ComplicationType private int[] mTypes;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        setTheme(android.R.style.Theme_Translucent_NoTitleBar);

        super.onCreate(savedInstanceState);

        Intent intent = getIntent();

        switch (Objects.requireNonNull(intent.getAction())) {
            case ACTION_START_PROVIDER_CHOOSER:
                mWatchFace =
                        intent.getParcelableExtra(
                                ProviderChooserIntent.EXTRA_WATCH_FACE_COMPONENT_NAME);
                mWfComplicationId =
                        intent.getIntExtra(ProviderChooserIntent.EXTRA_COMPLICATION_ID, 0);
                mTypes = intent.getIntArrayExtra(ProviderChooserIntent.EXTRA_SUPPORTED_TYPES);
                if (checkPermission()) {
                    startProviderChooser();
                } else {
                    ActivityCompat.requestPermissions(
                            this,
                            new String[] {COMPLICATIONS_PERMISSION},
                            PERMISSION_REQUEST_CODE_PROVIDER_CHOOSER);
                }
                break;
            case ACTION_PERMISSION_REQUEST_ONLY:
                mWatchFace =
                        intent.getParcelableExtra(
                                ProviderChooserIntent.EXTRA_WATCH_FACE_COMPONENT_NAME);
                if (checkPermission()) {
                    finish();
                } else {
                    ActivityCompat.requestPermissions(
                            this,
                            new String[] {COMPLICATIONS_PERMISSION},
                            PERMISSION_REQUEST_CODE_REQUEST_ONLY);
                }
                break;
            default:
                throw new IllegalStateException("Unrecognised intent action.");
        }
    }

    @Override
    public void onRequestPermissionsResult(
            int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        if (grantResults.length == 0) {
            // Request was cancelled.
            return;
        }
        if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            if (requestCode == PERMISSION_REQUEST_CODE_PROVIDER_CHOOSER) {
                startProviderChooser();
            } else {
                finish();
            }
            requestUpdateAll(mWatchFace);
        } else {
            finish();
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        if (requestCode == START_REQUEST_CODE_PROVIDER_CHOOSER) {
            setResult(resultCode, data);
            finish();
        }
    }

    private boolean checkPermission() {
        return ActivityCompat.checkSelfPermission(this, COMPLICATIONS_PERMISSION_PRIVILEGED)
                        == PackageManager.PERMISSION_GRANTED
                || ActivityCompat.checkSelfPermission(this, COMPLICATIONS_PERMISSION)
                        == PackageManager.PERMISSION_GRANTED;
    }

    /**
     * Returns an intent that may be used to start the provider chooser activity via the
     * ComplicationHelperActivity. This allows the required permission to be checked before the
     * provider chooser is displayed.
     *
     * <p>To use this, the ComplicationHelperActivity must be added to your app, and your app must
     * include the {@code com.google.android.wearable.permission.RECEIVE_COMPLICATION_DATA}
     * permission in its manifest.
     *
     * <p>The provider chooser activity will show a list of all providers that can supply data of at
     * least one of the {@code supportedTypes}.
     *
     * <p>When the user chooses a provider, the configuration will be set up in the complications
     * system - the watch face does not need to do anything else.
     *
     * <p>The activity may be started using {@link Activity#startActivityForResult}. The result
     * delivered back to your activity will have a result code of {@link Activity#RESULT_OK
     * RESULT_OK} if a provider was successfully set, or a result code of {@link
     * Activity#RESULT_CANCELED RESULT_CANCELED} if no provider was set. In the case where a
     * provider was set, {@link ComplicationProviderInfo} for the chosen provider will be included
     * in the data intent of the result, as an extra with the key
     * android.support.wearable.complications.EXTRA_PROVIDER_INFO.
     *
     * <p>The package of the calling app must match the package of the watch face, or this will not
     * work.
     *
     * @param context context for the current app, that must contain a ComplicationHelperActivity
     * @param watchFace the ComponentName of the WatchFaceService being configured.
     * @param watchFaceComplicationId the watch face's id for the complication being configured.
     *     This must match the id passed in when the watch face calls
     *     WatchFaceService.Engine#setActiveComplications.
     * @param supportedTypes the types supported by the complication, in decreasing order of
     *     preference. If a provider can supply data for more than one of these types, the type
     *     chosen will be whichever was specified first.
     */
    @NonNull
    public static Intent createProviderChooserHelperIntent(
            @NonNull Context context,
            @NonNull ComponentName watchFace,
            int watchFaceComplicationId,
            @NonNull int[] supportedTypes) {
        Intent intent = new Intent(context, ComplicationHelperActivity.class);
        intent.setAction(ACTION_START_PROVIDER_CHOOSER);
        intent.putExtra(ProviderChooserIntent.EXTRA_WATCH_FACE_COMPONENT_NAME, watchFace);
        intent.putExtra(ProviderChooserIntent.EXTRA_COMPLICATION_ID, watchFaceComplicationId);
        intent.putExtra(ProviderChooserIntent.EXTRA_SUPPORTED_TYPES, supportedTypes);
        return intent;
    }

    /**
     * Returns an intent that may be used to start this activity in order to request the permission
     * required to receive complication data.
     *
     * <p>To use this, the ComplicationHelperActivity must be added to your app, and your app must
     * include the {@code com.google.android.wearable.permission.RECEIVE_COMPLICATION_DATA}
     * permission in its manifest.
     *
     * <p>If the current app has already been granted this permission, the activity will finish
     * immediately.
     *
     * <p>If the current app has not been granted this permission, a permission request will be
     * made. If the permission is granted by the user, an update of all complications on the current
     * watch face will be triggered. The provided {@code watchFace} must match the current watch
     * face for this to occur.
     *
     * @param context context for the current app, that must contain a ComplicationHelperActivity
     * @param watchFace the ComponentName of the WatchFaceService for the current watch face
     */
    @NonNull
    public static Intent createPermissionRequestHelperIntent(
            @NonNull Context context, @NonNull ComponentName watchFace) {
        Intent intent = new Intent(context, ComplicationHelperActivity.class);
        intent.setAction(ACTION_PERMISSION_REQUEST_ONLY);
        intent.putExtra(ProviderChooserIntent.EXTRA_WATCH_FACE_COMPONENT_NAME, watchFace);
        return intent;
    }

    private void startProviderChooser() {
        startActivityForResult(
                ProviderChooserIntent.createProviderChooserIntent(
                        mWatchFace, mWfComplicationId, mTypes),
                START_REQUEST_CODE_PROVIDER_CHOOSER);
    }

    /** Requests that the system update all active complications on the watch face. */
    private void requestUpdateAll(ComponentName watchFaceComponent) {
        Intent intent = new Intent(ACTION_REQUEST_UPDATE_ALL_ACTIVE);
        intent.setPackage(UPDATE_REQUEST_RECEIVER_PACKAGE);
        intent.putExtra(EXTRA_WATCH_FACE_COMPONENT, watchFaceComponent);
        // Add a placeholder PendingIntent to allow the UID to be checked.
        intent.putExtra(
                ProviderUpdateRequesterConstants.EXTRA_PENDING_INTENT,
                PendingIntent.getActivity(this, 0, new Intent(""), 0));
        sendBroadcast(intent);
    }
}