1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy of 6 * the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations under 14 * the License. 15 */ 16 17 package android.view.inputmethod; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.content.ClipDescription; 22 import android.net.Uri; 23 import android.os.Parcel; 24 import android.os.Parcelable; 25 import android.os.RemoteException; 26 27 import com.android.internal.inputmethod.IInputContentUriToken; 28 29 import java.security.InvalidParameterException; 30 31 /** 32 * A container object with which input methods can send content files to the target application. 33 */ 34 public final class InputContentInfo implements Parcelable { 35 36 @NonNull 37 private final Uri mContentUri; 38 @NonNull 39 private final ClipDescription mDescription; 40 @Nullable 41 private final Uri mLinkUri; 42 @NonNull 43 private IInputContentUriToken mUriToken; 44 45 /** 46 * Constructs {@link InputContentInfo} object only with mandatory data. 47 * 48 * @param contentUri Content URI to be exported from the input method. 49 * This cannot be {@code null}. 50 * @param description A {@link ClipDescription} object that contains the metadata of 51 * {@code contentUri} such as MIME type(s). This object cannot be {@code null}. Also 52 * {@link ClipDescription#getLabel()} should be describing the content specified by 53 * {@code contentUri} for accessibility reasons. 54 */ 55 public InputContentInfo(@NonNull Uri contentUri, @NonNull ClipDescription description) { 56 this(contentUri, description, null /* link Uri */); 57 } 58 59 /** 60 * Constructs {@link InputContentInfo} object with additional link URI. 61 * 62 * @param contentUri Content URI to be exported from the input method. 63 * This cannot be {@code null}. 64 * @param description A {@link ClipDescription} object that contains the metadata of 65 * {@code contentUri} such as MIME type(s). This object cannot be {@code null}. Also 66 * {@link ClipDescription#getLabel()} should be describing the content specified by 67 * {@code contentUri} for accessibility reasons. 68 * @param linkUri An optional {@code http} or {@code https} URI. The editor author may provide 69 * a way to navigate the user to the specified web page if this is not {@code null}. 70 * @throws InvalidParameterException if any invalid parameter is specified. 71 */ 72 public InputContentInfo(@NonNull Uri contentUri, @NonNull ClipDescription description, 73 @Nullable Uri linkUri) { 74 validateInternal(contentUri, description, linkUri, true /* throwException */); 75 mContentUri = contentUri; 76 mDescription = description; 77 mLinkUri = linkUri; 78 } 79 80 /** 81 * @return {@code true} if all the fields are valid. 82 * @hide 83 */ 84 public boolean validate() { 85 return validateInternal(mContentUri, mDescription, mLinkUri, false /* throwException */); 86 } 87 88 /** 89 * Constructs {@link InputContentInfo} object with additional link URI. 90 * 91 * @param contentUri Content URI to be exported from the input method. 92 * This cannot be {@code null}. 93 * @param description A {@link ClipDescription} object that contains the metadata of 94 * {@code contentUri} such as MIME type(s). This object cannot be {@code null}. Also 95 * {@link ClipDescription#getLabel()} should be describing the content specified by 96 * {@code contentUri} for accessibility reasons. 97 * @param linkUri An optional {@code http} or {@code https} URI. The editor author may provide 98 * a way to navigate the user to the specified web page if this is not {@code null}. 99 * @param throwException {@code true} if this method should throw an 100 * {@link InvalidParameterException}. 101 * @throws InvalidParameterException if any invalid parameter is specified. 102 */ 103 private static boolean validateInternal(@NonNull Uri contentUri, 104 @NonNull ClipDescription description, @Nullable Uri linkUri, boolean throwException) { 105 if (contentUri == null) { 106 if (throwException) { 107 throw new NullPointerException("contentUri"); 108 } 109 return false; 110 } 111 if (description == null) { 112 if (throwException) { 113 throw new NullPointerException("description"); 114 } 115 return false; 116 } 117 final String contentUriScheme = contentUri.getScheme(); 118 if (!"content".equals(contentUriScheme)) { 119 if (throwException) { 120 throw new InvalidParameterException("contentUri must have content scheme"); 121 } 122 return false; 123 } 124 if (linkUri != null) { 125 final String scheme = linkUri.getScheme(); 126 if (scheme == null || 127 (!scheme.equalsIgnoreCase("http") && !scheme.equalsIgnoreCase("https"))) { 128 if (throwException) { 129 throw new InvalidParameterException( 130 "linkUri must have either http or https scheme"); 131 } 132 return false; 133 } 134 } 135 return true; 136 } 137 138 /** 139 * @return Content URI with which the content can be obtained. 140 */ 141 @NonNull 142 public Uri getContentUri() { return mContentUri; } 143 144 /** 145 * @return {@link ClipDescription} object that contains the metadata of {@code #getContentUri()} 146 * such as MIME type(s). {@link ClipDescription#getLabel()} can be used for accessibility 147 * purpose. 148 */ 149 @NonNull 150 public ClipDescription getDescription() { return mDescription; } 151 152 /** 153 * @return An optional {@code http} or {@code https} URI that is related to this content. 154 */ 155 @Nullable 156 public Uri getLinkUri() { return mLinkUri; } 157 158 void setUriToken(IInputContentUriToken token) { 159 if (mUriToken != null) { 160 throw new IllegalStateException("URI token is already set"); 161 } 162 mUriToken = token; 163 } 164 165 /** 166 * Requests a temporary read-only access permission for content URI associated with this object. 167 * 168 * <p>Does nothing if the temporary permission is already granted.</p> 169 */ 170 public void requestPermission() { 171 if (mUriToken == null) { 172 return; 173 } 174 try { 175 mUriToken.take(); 176 } catch (RemoteException e) { 177 e.rethrowFromSystemServer(); 178 } 179 } 180 181 /** 182 * Releases a temporary read-only access permission for content URI associated with this object. 183 * 184 * <p>Does nothing if the temporary permission is not granted.</p> 185 */ 186 public void releasePermission() { 187 if (mUriToken == null) { 188 return; 189 } 190 try { 191 mUriToken.release(); 192 } catch (RemoteException e) { 193 e.rethrowFromSystemServer(); 194 } 195 } 196 197 /** 198 * Used to package this object into a {@link Parcel}. 199 * 200 * @param dest The {@link Parcel} to be written. 201 * @param flags The flags used for parceling. 202 */ 203 @Override 204 public void writeToParcel(Parcel dest, int flags) { 205 Uri.writeToParcel(dest, mContentUri); 206 mDescription.writeToParcel(dest, flags); 207 Uri.writeToParcel(dest, mLinkUri); 208 if (mUriToken != null) { 209 dest.writeInt(1); 210 dest.writeStrongBinder(mUriToken.asBinder()); 211 } else { 212 dest.writeInt(0); 213 } 214 } 215 216 private InputContentInfo(@NonNull Parcel source) { 217 mContentUri = Uri.CREATOR.createFromParcel(source); 218 mDescription = ClipDescription.CREATOR.createFromParcel(source); 219 mLinkUri = Uri.CREATOR.createFromParcel(source); 220 if (source.readInt() == 1) { 221 mUriToken = IInputContentUriToken.Stub.asInterface(source.readStrongBinder()); 222 } else { 223 mUriToken = null; 224 } 225 } 226 227 /** 228 * Used to make this class parcelable. 229 */ 230 public static final Parcelable.Creator<InputContentInfo> CREATOR 231 = new Parcelable.Creator<InputContentInfo>() { 232 @Override 233 public InputContentInfo createFromParcel(Parcel source) { 234 return new InputContentInfo(source); 235 } 236 237 @Override 238 public InputContentInfo[] newArray(int size) { 239 return new InputContentInfo[size]; 240 } 241 }; 242 243 /** 244 * {@inheritDoc} 245 */ 246 @Override 247 public int describeContents() { 248 return 0; 249 } 250 } 251