blob: b39705e0b1fa686b1d6ceabc3ee9a1e10a5e27df [file] [log] [blame]
/*
* Copyright (C) 2016 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 android.view.inputmethod;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ClipDescription;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
import com.android.internal.inputmethod.IInputContentUriToken;
import java.security.InvalidParameterException;
/**
* A container object with which input methods can send content files to the target application.
*/
public final class InputContentInfo implements Parcelable {
@NonNull
private final Uri mContentUri;
@NonNull
private final ClipDescription mDescription;
@Nullable
private final Uri mLinkUri;
@NonNull
private IInputContentUriToken mUriToken;
/**
* Constructs {@link InputContentInfo} object only with mandatory data.
*
* @param contentUri Content URI to be exported from the input method.
* This cannot be {@code null}.
* @param description A {@link ClipDescription} object that contains the metadata of
* {@code contentUri} such as MIME type(s). This object cannot be {@code null}. Also
* {@link ClipDescription#getLabel()} should be describing the content specified by
* {@code contentUri} for accessibility reasons.
*/
public InputContentInfo(@NonNull Uri contentUri, @NonNull ClipDescription description) {
this(contentUri, description, null /* link Uri */);
}
/**
* Constructs {@link InputContentInfo} object with additional link URI.
*
* @param contentUri Content URI to be exported from the input method.
* This cannot be {@code null}.
* @param description A {@link ClipDescription} object that contains the metadata of
* {@code contentUri} such as MIME type(s). This object cannot be {@code null}. Also
* {@link ClipDescription#getLabel()} should be describing the content specified by
* {@code contentUri} for accessibility reasons.
* @param linkUri An optional {@code http} or {@code https} URI. The editor author may provide
* a way to navigate the user to the specified web page if this is not {@code null}.
* @throws InvalidParameterException if any invalid parameter is specified.
*/
public InputContentInfo(@NonNull Uri contentUri, @NonNull ClipDescription description,
@Nullable Uri linkUri) {
validateInternal(contentUri, description, linkUri, true /* throwException */);
mContentUri = contentUri;
mDescription = description;
mLinkUri = linkUri;
}
/**
* @return {@code true} if all the fields are valid.
* @hide
*/
public boolean validate() {
return validateInternal(mContentUri, mDescription, mLinkUri, false /* throwException */);
}
/**
* Constructs {@link InputContentInfo} object with additional link URI.
*
* @param contentUri Content URI to be exported from the input method.
* This cannot be {@code null}.
* @param description A {@link ClipDescription} object that contains the metadata of
* {@code contentUri} such as MIME type(s). This object cannot be {@code null}. Also
* {@link ClipDescription#getLabel()} should be describing the content specified by
* {@code contentUri} for accessibility reasons.
* @param linkUri An optional {@code http} or {@code https} URI. The editor author may provide
* a way to navigate the user to the specified web page if this is not {@code null}.
* @param throwException {@code true} if this method should throw an
* {@link InvalidParameterException}.
* @throws InvalidParameterException if any invalid parameter is specified.
*/
private static boolean validateInternal(@NonNull Uri contentUri,
@NonNull ClipDescription description, @Nullable Uri linkUri, boolean throwException) {
if (contentUri == null) {
if (throwException) {
throw new NullPointerException("contentUri");
}
return false;
}
if (description == null) {
if (throwException) {
throw new NullPointerException("description");
}
return false;
}
final String contentUriScheme = contentUri.getScheme();
if (!"content".equals(contentUriScheme)) {
if (throwException) {
throw new InvalidParameterException("contentUri must have content scheme");
}
return false;
}
if (linkUri != null) {
final String scheme = linkUri.getScheme();
if (scheme == null ||
(!scheme.equalsIgnoreCase("http") && !scheme.equalsIgnoreCase("https"))) {
if (throwException) {
throw new InvalidParameterException(
"linkUri must have either http or https scheme");
}
return false;
}
}
return true;
}
/**
* @return Content URI with which the content can be obtained.
*/
@NonNull
public Uri getContentUri() { return mContentUri; }
/**
* @return {@link ClipDescription} object that contains the metadata of {@code #getContentUri()}
* such as MIME type(s). {@link ClipDescription#getLabel()} can be used for accessibility
* purpose.
*/
@NonNull
public ClipDescription getDescription() { return mDescription; }
/**
* @return An optional {@code http} or {@code https} URI that is related to this content.
*/
@Nullable
public Uri getLinkUri() { return mLinkUri; }
void setUriToken(IInputContentUriToken token) {
if (mUriToken != null) {
throw new IllegalStateException("URI token is already set");
}
mUriToken = token;
}
/**
* Requests a temporary read-only access permission for content URI associated with this object.
*
* <p>Does nothing if the temporary permission is already granted.</p>
*/
public void requestPermission() {
if (mUriToken == null) {
return;
}
try {
mUriToken.take();
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
}
/**
* Releases a temporary read-only access permission for content URI associated with this object.
*
* <p>Does nothing if the temporary permission is not granted.</p>
*/
public void releasePermission() {
if (mUriToken == null) {
return;
}
try {
mUriToken.release();
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
}
/**
* Used to package this object into a {@link Parcel}.
*
* @param dest The {@link Parcel} to be written.
* @param flags The flags used for parceling.
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
Uri.writeToParcel(dest, mContentUri);
mDescription.writeToParcel(dest, flags);
Uri.writeToParcel(dest, mLinkUri);
if (mUriToken != null) {
dest.writeInt(1);
dest.writeStrongBinder(mUriToken.asBinder());
} else {
dest.writeInt(0);
}
}
private InputContentInfo(@NonNull Parcel source) {
mContentUri = Uri.CREATOR.createFromParcel(source);
mDescription = ClipDescription.CREATOR.createFromParcel(source);
mLinkUri = Uri.CREATOR.createFromParcel(source);
if (source.readInt() == 1) {
mUriToken = IInputContentUriToken.Stub.asInterface(source.readStrongBinder());
} else {
mUriToken = null;
}
}
/**
* Used to make this class parcelable.
*/
public static final Parcelable.Creator<InputContentInfo> CREATOR
= new Parcelable.Creator<InputContentInfo>() {
@Override
public InputContentInfo createFromParcel(Parcel source) {
return new InputContentInfo(source);
}
@Override
public InputContentInfo[] newArray(int size) {
return new InputContentInfo[size];
}
};
/**
* {@inheritDoc}
*/
@Override
public int describeContents() {
return 0;
}
}