Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3d96aedc authored by Evan Chen's avatar Evan Chen Committed by Android (Google) Code Review
Browse files

Merge "[W]SelfManaged Association device icon" into main

parents c1671d68 6f5a0ad1
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -9834,6 +9834,7 @@ package android.companion {
  public final class AssociationInfo implements android.os.Parcelable {
    method public int describeContents();
    method @Nullable public android.companion.AssociatedDevice getAssociatedDevice();
    method @FlaggedApi("android.companion.association_device_icon") @Nullable public android.graphics.drawable.Icon getDeviceIcon();
    method @Nullable public android.net.MacAddress getDeviceMacAddress();
    method @Nullable public String getDeviceProfile();
    method @Nullable public CharSequence getDisplayName();
@@ -9847,6 +9848,7 @@ package android.companion {
  public final class AssociationRequest implements android.os.Parcelable {
    method public int describeContents();
    method @FlaggedApi("android.companion.association_device_icon") @Nullable public android.graphics.drawable.Icon getDeviceIcon();
    method @Nullable public String getDeviceProfile();
    method @Nullable public CharSequence getDisplayName();
    method public boolean isForceConfirmation();
@@ -9866,6 +9868,7 @@ package android.companion {
    ctor public AssociationRequest.Builder();
    method @NonNull public android.companion.AssociationRequest.Builder addDeviceFilter(@Nullable android.companion.DeviceFilter<?>);
    method @NonNull public android.companion.AssociationRequest build();
    method @FlaggedApi("android.companion.association_device_icon") @NonNull @RequiresPermission(android.Manifest.permission.REQUEST_COMPANION_SELF_MANAGED) public android.companion.AssociationRequest.Builder setDeviceIcon(@NonNull android.graphics.drawable.Icon);
    method @NonNull public android.companion.AssociationRequest.Builder setDeviceProfile(@NonNull String);
    method @NonNull public android.companion.AssociationRequest.Builder setDisplayName(@NonNull CharSequence);
    method @NonNull @RequiresPermission(android.Manifest.permission.REQUEST_COMPANION_SELF_MANAGED) public android.companion.AssociationRequest.Builder setForceConfirmation(boolean);
+1 −0
Original line number Diff line number Diff line
@@ -914,6 +914,7 @@ package android.companion {
    ctor public AssociationInfo.Builder(@NonNull android.companion.AssociationInfo);
    method @NonNull public android.companion.AssociationInfo build();
    method @NonNull public android.companion.AssociationInfo.Builder setAssociatedDevice(@Nullable android.companion.AssociatedDevice);
    method @FlaggedApi("android.companion.association_device_icon") @NonNull public android.companion.AssociationInfo.Builder setDeviceIcon(@Nullable android.graphics.drawable.Icon);
    method @NonNull public android.companion.AssociationInfo.Builder setDeviceMacAddress(@Nullable android.net.MacAddress);
    method @NonNull public android.companion.AssociationInfo.Builder setDeviceProfile(@Nullable String);
    method @NonNull public android.companion.AssociationInfo.Builder setDisplayName(@Nullable CharSequence);
+52 −4
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.annotation.UserIdInt;
import android.graphics.drawable.Icon;
import android.net.MacAddress;
import android.os.Parcel;
import android.os.Parcelable;
@@ -85,6 +86,11 @@ public final class AssociationInfo implements Parcelable {
    private final long mLastTimeConnectedMs;
    private final int mSystemDataSyncFlags;

    /**
     * A device icon displayed on a selfManaged association dialog.
     */
    private final Icon mDeviceIcon;

    /**
     * Creates a new Association.
     *
@@ -95,7 +101,7 @@ public final class AssociationInfo implements Parcelable {
            @Nullable CharSequence displayName, @Nullable String deviceProfile,
            @Nullable AssociatedDevice associatedDevice, boolean selfManaged,
            boolean notifyOnDeviceNearby, boolean revoked, boolean pending, long timeApprovedMs,
            long lastTimeConnectedMs, int systemDataSyncFlags) {
            long lastTimeConnectedMs, int systemDataSyncFlags, @Nullable Icon deviceIcon) {
        if (id <= 0) {
            throw new IllegalArgumentException("Association ID should be greater than 0");
        }
@@ -119,6 +125,7 @@ public final class AssociationInfo implements Parcelable {
        mTimeApprovedMs = timeApprovedMs;
        mLastTimeConnectedMs = lastTimeConnectedMs;
        mSystemDataSyncFlags = systemDataSyncFlags;
        mDeviceIcon = deviceIcon;
    }

    /**
@@ -277,6 +284,20 @@ public final class AssociationInfo implements Parcelable {
        return mSystemDataSyncFlags;
    }

    /**
     * Get the device icon of the associated device. The device icon represents the device type.
     *
     * @return the device icon, or {@code null} if no device icon is has been set for the
     * associated device.
     *
     * @see AssociationRequest.Builder#setDeviceIcon(Icon)
     */
    @FlaggedApi(Flags.FLAG_ASSOCIATION_DEVICE_ICON)
    @Nullable
    public Icon getDeviceIcon() {
        return mDeviceIcon;
    }

    /**
     * Utility method for checking if the association represents a device with the given MAC
     * address.
@@ -370,14 +391,16 @@ public final class AssociationInfo implements Parcelable {
                && Objects.equals(mDisplayName, that.mDisplayName)
                && Objects.equals(mDeviceProfile, that.mDeviceProfile)
                && Objects.equals(mAssociatedDevice, that.mAssociatedDevice)
                && mSystemDataSyncFlags == that.mSystemDataSyncFlags;
                && mSystemDataSyncFlags == that.mSystemDataSyncFlags
                && (mDeviceIcon == null ? that.mDeviceIcon == null
                : mDeviceIcon.sameAs(that.mDeviceIcon));
    }

    @Override
    public int hashCode() {
        return Objects.hash(mId, mUserId, mPackageName, mTag, mDeviceMacAddress, mDisplayName,
                mDeviceProfile, mAssociatedDevice, mSelfManaged, mNotifyOnDeviceNearby, mRevoked,
                mPending, mTimeApprovedMs, mLastTimeConnectedMs, mSystemDataSyncFlags);
                mPending, mTimeApprovedMs, mLastTimeConnectedMs, mSystemDataSyncFlags, mDeviceIcon);
    }

    @Override
@@ -402,6 +425,12 @@ public final class AssociationInfo implements Parcelable {
        dest.writeLong(mTimeApprovedMs);
        dest.writeLong(mLastTimeConnectedMs);
        dest.writeInt(mSystemDataSyncFlags);
        if (mDeviceIcon != null) {
            dest.writeInt(1);
            mDeviceIcon.writeToParcel(dest, flags);
        } else {
            dest.writeInt(0);
        }
    }

    private AssociationInfo(@NonNull Parcel in) {
@@ -420,6 +449,11 @@ public final class AssociationInfo implements Parcelable {
        mTimeApprovedMs = in.readLong();
        mLastTimeConnectedMs = in.readLong();
        mSystemDataSyncFlags = in.readInt();
        if (in.readInt() == 1) {
            mDeviceIcon = Icon.CREATOR.createFromParcel(in);
        } else {
            mDeviceIcon = null;
        }
    }

    @NonNull
@@ -459,6 +493,7 @@ public final class AssociationInfo implements Parcelable {
        private long mTimeApprovedMs;
        private long mLastTimeConnectedMs;
        private int mSystemDataSyncFlags;
        private Icon mDeviceIcon;

        /** @hide */
        @TestApi
@@ -486,6 +521,7 @@ public final class AssociationInfo implements Parcelable {
            mTimeApprovedMs = info.mTimeApprovedMs;
            mLastTimeConnectedMs = info.mLastTimeConnectedMs;
            mSystemDataSyncFlags = info.mSystemDataSyncFlags;
            mDeviceIcon = info.mDeviceIcon;
        }

        /**
@@ -510,6 +546,7 @@ public final class AssociationInfo implements Parcelable {
            mTimeApprovedMs = info.mTimeApprovedMs;
            mLastTimeConnectedMs = info.mLastTimeConnectedMs;
            mSystemDataSyncFlags = info.mSystemDataSyncFlags;
            mDeviceIcon = info.mDeviceIcon;
        }

        /** @hide */
@@ -622,6 +659,16 @@ public final class AssociationInfo implements Parcelable {
            return this;
        }

        /** @hide */
        @TestApi
        @NonNull
        @SuppressLint("MissingGetterMatchingBuilder")
        @FlaggedApi(Flags.FLAG_ASSOCIATION_DEVICE_ICON)
        public Builder setDeviceIcon(@Nullable Icon deviceIcon) {
            mDeviceIcon = deviceIcon;
            return this;
        }

        /** @hide */
        @TestApi
        @NonNull
@@ -648,7 +695,8 @@ public final class AssociationInfo implements Parcelable {
                    mPending,
                    mTimeApprovedMs,
                    mLastTimeConnectedMs,
                    mSystemDataSyncFlags
                    mSystemDataSyncFlags,
                    mDeviceIcon
            );
        }
    }
+60 −4
Original line number Diff line number Diff line
@@ -23,12 +23,14 @@ import static com.android.internal.util.CollectionUtils.emptyIfNull;
import static java.util.Objects.requireNonNull;

import android.Manifest;
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.StringDef;
import android.annotation.UserIdInt;
import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.drawable.Icon;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
@@ -233,6 +235,13 @@ public final class AssociationRequest implements Parcelable {
     */
    private boolean mSkipPrompt;

    /**
     * The device icon displayed in selfManaged association dialog.
     * @hide
     */
    @Nullable
    private Icon mDeviceIcon;

    /**
     * Creates a new AssociationRequest.
     *
@@ -258,15 +267,16 @@ public final class AssociationRequest implements Parcelable {
            @Nullable @DeviceProfile String deviceProfile,
            @Nullable CharSequence displayName,
            boolean selfManaged,
            boolean forceConfirmation) {
            boolean forceConfirmation,
            @Nullable Icon deviceIcon) {
        mSingleDevice = singleDevice;
        mDeviceFilters = requireNonNull(deviceFilters);
        mDeviceProfile = deviceProfile;
        mDisplayName = displayName;
        mSelfManaged = selfManaged;
        mForceConfirmation = forceConfirmation;

        mCreationTime = System.currentTimeMillis();
        mDeviceIcon = deviceIcon;
    }

    /**
@@ -318,6 +328,19 @@ public final class AssociationRequest implements Parcelable {
        return mSingleDevice;
    }

    /**
     * Get the device icon of the self-managed association request.
     *
     * @return the device icon, or {@code null} if no device icon has been set.
     *
     * @see Builder#setDeviceIcon(Icon)
     */
    @FlaggedApi(Flags.FLAG_ASSOCIATION_DEVICE_ICON)
    @Nullable
    public Icon getDeviceIcon() {
        return mDeviceIcon;
    }

    /** @hide */
    public void setPackageName(@NonNull String packageName) {
        mPackageName = packageName;
@@ -365,6 +388,7 @@ public final class AssociationRequest implements Parcelable {
        private CharSequence mDisplayName;
        private boolean mSelfManaged = false;
        private boolean mForceConfirmation = false;
        private Icon mDeviceIcon = null;

        public Builder() {}

@@ -450,6 +474,23 @@ public final class AssociationRequest implements Parcelable {
            return this;
        }

        /**
         * Set the device icon for the self-managed device and this icon will be
         * displayed in the self-managed association dialog.
         *
         * @throws IllegalArgumentException if the icon is not exactly 24dp by 24dp
         * or if it is {@link Icon#TYPE_URI} or {@link Icon#TYPE_URI_ADAPTIVE_BITMAP}.
         * @see #setSelfManaged(boolean)
         */
        @NonNull
        @RequiresPermission(REQUEST_COMPANION_SELF_MANAGED)
        @FlaggedApi(Flags.FLAG_ASSOCIATION_DEVICE_ICON)
        public Builder setDeviceIcon(@NonNull Icon deviceIcon) {
            checkNotUsed();
            mDeviceIcon = requireNonNull(deviceIcon);
            return this;
        }

        /** @inheritDoc */
        @NonNull
        @Override
@@ -460,7 +501,7 @@ public final class AssociationRequest implements Parcelable {
                        + "provide the display name of the device");
            }
            return new AssociationRequest(mSingleDevice, emptyIfNull(mDeviceFilters),
                    mDeviceProfile, mDisplayName, mSelfManaged, mForceConfirmation);
                    mDeviceProfile, mDisplayName, mSelfManaged, mForceConfirmation, mDeviceIcon);
        }
    }

@@ -561,7 +602,9 @@ public final class AssociationRequest implements Parcelable {
                && Objects.equals(mDeviceProfilePrivilegesDescription,
                        that.mDeviceProfilePrivilegesDescription)
                && mCreationTime == that.mCreationTime
                && mSkipPrompt == that.mSkipPrompt;
                && mSkipPrompt == that.mSkipPrompt
                && (mDeviceIcon == null ? that.mDeviceIcon == null
                : mDeviceIcon.sameAs(that.mDeviceIcon));
    }

    @Override
@@ -579,6 +622,8 @@ public final class AssociationRequest implements Parcelable {
        _hash = 31 * _hash + Objects.hashCode(mDeviceProfilePrivilegesDescription);
        _hash = 31 * _hash + Long.hashCode(mCreationTime);
        _hash = 31 * _hash + Boolean.hashCode(mSkipPrompt);
        _hash = 31 * _hash + Objects.hashCode(mDeviceIcon);

        return _hash;
    }

@@ -606,6 +651,12 @@ public final class AssociationRequest implements Parcelable {
            dest.writeString8(mDeviceProfilePrivilegesDescription);
        }
        dest.writeLong(mCreationTime);
        if (mDeviceIcon != null) {
            dest.writeInt(1);
            mDeviceIcon.writeToParcel(dest, flags);
        } else {
            dest.writeInt(0);
        }
    }

    @Override
@@ -650,6 +701,11 @@ public final class AssociationRequest implements Parcelable {
        this.mDeviceProfilePrivilegesDescription = deviceProfilePrivilegesDescription;
        this.mCreationTime = creationTime;
        this.mSkipPrompt = skipPrompt;
        if (in.readInt() == 1) {
            mDeviceIcon = Icon.CREATOR.createFromParcel(in);
        } else {
            mDeviceIcon = null;
        }
    }

    @NonNull
+44 −0
Original line number Diff line number Diff line
@@ -20,6 +20,8 @@ import static android.Manifest.permission.REQUEST_COMPANION_PROFILE_APP_STREAMIN
import static android.Manifest.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION;
import static android.Manifest.permission.REQUEST_COMPANION_PROFILE_COMPUTER;
import static android.Manifest.permission.REQUEST_COMPANION_PROFILE_WATCH;
import static android.graphics.drawable.Icon.TYPE_URI;
import static android.graphics.drawable.Icon.TYPE_URI_ADAPTIVE_BITMAP;


import android.annotation.CallbackExecutor;
@@ -49,6 +51,11 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.graphics.drawable.VectorDrawable;
import android.net.MacAddress;
import android.os.Binder;
import android.os.Handler;
@@ -535,6 +542,13 @@ public final class CompanionDeviceManager {
        Objects.requireNonNull(executor, "Executor cannot be null");
        Objects.requireNonNull(callback, "Callback cannot be null");

        final Icon deviceIcon = request.getDeviceIcon();

        if (deviceIcon != null && !isValidIcon(deviceIcon, mContext)) {
            throw new IllegalArgumentException("The size of the device icon must be 24dp x 24dp to"
                    + "ensure proper display");
        }

        try {
            mService.associate(request, new AssociationRequestCallbackProxy(executor, callback),
                    mContext.getOpPackageName(), mContext.getUserId());
@@ -2027,4 +2041,34 @@ public final class CompanionDeviceManager {
            }
        }
    }

    private boolean isValidIcon(Icon icon, Context context) {
        if (icon.getType() == TYPE_URI_ADAPTIVE_BITMAP || icon.getType() == TYPE_URI) {
            throw new IllegalArgumentException("The URI based Icon is not supported.");
        }
        Drawable drawable = icon.loadDrawable(context);
        float density = context.getResources().getDisplayMetrics().density;

        if (drawable instanceof BitmapDrawable) {
            Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();

            float widthDp = bitmap.getWidth() / density;
            float heightDp = bitmap.getHeight() / density;

            if (widthDp != 24 || heightDp != 24) {
                return false;
            }
        } else if (drawable instanceof VectorDrawable) {
            VectorDrawable vectorDrawable = (VectorDrawable) drawable;
            float widthDp = vectorDrawable.getIntrinsicWidth() / density;
            float heightDp = vectorDrawable.getIntrinsicHeight() / density;

            if (widthDp != 24 || heightDp != 24) {
                return false;
            }
        } else {
            throw new IllegalArgumentException("The format of the device icon is unsupported.");
        }
        return true;
    }
}
Loading