1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of 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, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package androidx.core.view.inputmethod; 18 19 import android.content.ClipDescription; 20 import android.net.Uri; 21 import android.os.Build; 22 import android.view.inputmethod.InputContentInfo; 23 24 import androidx.annotation.RequiresApi; 25 26 import org.jspecify.annotations.NonNull; 27 import org.jspecify.annotations.Nullable; 28 29 /** 30 * Helper for accessing features in InputContentInfo introduced after API level 13 in a backwards 31 * compatible fashion. 32 */ 33 public final class InputContentInfoCompat { 34 35 private interface InputContentInfoCompatImpl { getContentUri()36 @NonNull Uri getContentUri(); 37 getDescription()38 @NonNull ClipDescription getDescription(); 39 getLinkUri()40 @Nullable Uri getLinkUri(); 41 getInputContentInfo()42 @Nullable Object getInputContentInfo(); 43 requestPermission()44 void requestPermission(); 45 releasePermission()46 void releasePermission(); 47 } 48 49 private static final class InputContentInfoCompatBaseImpl 50 implements InputContentInfoCompatImpl { 51 private final @NonNull Uri mContentUri; 52 private final @NonNull ClipDescription mDescription; 53 private final @Nullable Uri mLinkUri; 54 InputContentInfoCompatBaseImpl(@onNull Uri contentUri, @NonNull ClipDescription description, @Nullable Uri linkUri)55 InputContentInfoCompatBaseImpl(@NonNull Uri contentUri, 56 @NonNull ClipDescription description, @Nullable Uri linkUri) { 57 mContentUri = contentUri; 58 mDescription = description; 59 mLinkUri = linkUri; 60 } 61 62 @Override getContentUri()63 public @NonNull Uri getContentUri() { 64 return mContentUri; 65 } 66 67 @Override getDescription()68 public @NonNull ClipDescription getDescription() { 69 return mDescription; 70 } 71 72 @Override getLinkUri()73 public @Nullable Uri getLinkUri() { 74 return mLinkUri; 75 } 76 77 @Override getInputContentInfo()78 public @Nullable Object getInputContentInfo() { 79 return null; 80 } 81 82 @Override requestPermission()83 public void requestPermission() { 84 } 85 86 @Override releasePermission()87 public void releasePermission() { 88 } 89 } 90 91 @RequiresApi(25) 92 private static final class InputContentInfoCompatApi25Impl 93 implements InputContentInfoCompatImpl { 94 final @NonNull InputContentInfo mObject; 95 InputContentInfoCompatApi25Impl(@onNull Object inputContentInfo)96 InputContentInfoCompatApi25Impl(@NonNull Object inputContentInfo) { 97 mObject = (InputContentInfo) inputContentInfo; 98 } 99 InputContentInfoCompatApi25Impl(@onNull Uri contentUri, @NonNull ClipDescription description, @Nullable Uri linkUri)100 InputContentInfoCompatApi25Impl(@NonNull Uri contentUri, 101 @NonNull ClipDescription description, @Nullable Uri linkUri) { 102 mObject = new InputContentInfo(contentUri, description, linkUri); 103 } 104 105 @Override getContentUri()106 public @NonNull Uri getContentUri() { 107 return mObject.getContentUri(); 108 } 109 110 @Override getDescription()111 public @NonNull ClipDescription getDescription() { 112 return mObject.getDescription(); 113 } 114 115 @Override getLinkUri()116 public @Nullable Uri getLinkUri() { 117 return mObject.getLinkUri(); 118 } 119 120 @Override getInputContentInfo()121 public @NonNull Object getInputContentInfo() { 122 return mObject; 123 } 124 125 @Override requestPermission()126 public void requestPermission() { 127 mObject.requestPermission(); 128 } 129 130 @Override releasePermission()131 public void releasePermission() { 132 mObject.releasePermission(); 133 } 134 } 135 136 private final InputContentInfoCompatImpl mImpl; 137 138 /** 139 * Constructs {@link InputContentInfoCompat}. 140 * 141 * @param contentUri content URI to be exported from the input method. This cannot be 142 * {@code null}. 143 * @param description a {@link ClipDescription} object that contains the metadata of 144 * {@code contentUri} such as MIME type(s). This object cannot be 145 * {@code null}. Also {@link ClipDescription#getLabel()} should be describing 146 * the content specified by {@code contentUri} for accessibility reasons. 147 * @param linkUri an optional {@code http} or {@code https} URI. The editor author may provide 148 * a way to navigate the user to the specified web page if this is not 149 * {@code null}. 150 */ InputContentInfoCompat(@onNull Uri contentUri, @NonNull ClipDescription description, @Nullable Uri linkUri)151 public InputContentInfoCompat(@NonNull Uri contentUri, 152 @NonNull ClipDescription description, @Nullable Uri linkUri) { 153 if (Build.VERSION.SDK_INT >= 25) { 154 mImpl = new InputContentInfoCompatApi25Impl(contentUri, description, linkUri); 155 } else { 156 mImpl = new InputContentInfoCompatBaseImpl(contentUri, description, linkUri); 157 } 158 } 159 InputContentInfoCompat(@onNull InputContentInfoCompatImpl impl)160 private InputContentInfoCompat(@NonNull InputContentInfoCompatImpl impl) { 161 mImpl = impl; 162 } 163 164 /** 165 * @return content URI with which the content can be obtained. 166 */ getContentUri()167 public @NonNull Uri getContentUri() { 168 return mImpl.getContentUri(); 169 } 170 171 /** 172 * @return {@link ClipDescription} object that contains the metadata of {@code #getContentUri()} 173 * such as MIME type(s). {@link ClipDescription#getLabel()} can be used for accessibility 174 * purpose. 175 */ getDescription()176 public @NonNull ClipDescription getDescription() { 177 return mImpl.getDescription(); 178 } 179 180 /** 181 * @return an optional {@code http} or {@code https} URI that is related to this content. 182 */ getLinkUri()183 public @Nullable Uri getLinkUri() { 184 return mImpl.getLinkUri(); 185 } 186 187 /** 188 * Creates an instance from a framework android.view.inputmethod.InputContentInfo object. 189 * 190 * <p>This method always returns {@code null} on API <= 24.</p> 191 * 192 * @param inputContentInfo an android.view.inputmethod.InputContentInfo object, or {@code null} 193 * if none. 194 * @return an equivalent {@link InputContentInfoCompat} object, or {@code null} if not 195 * supported. 196 */ wrap(@ullable Object inputContentInfo)197 public static @Nullable InputContentInfoCompat wrap(@Nullable Object inputContentInfo) { 198 if (inputContentInfo == null) { 199 return null; 200 } 201 if (Build.VERSION.SDK_INT < 25) { 202 return null; 203 } 204 return new InputContentInfoCompat(new InputContentInfoCompatApi25Impl(inputContentInfo)); 205 } 206 207 /** 208 * Gets the underlying framework android.view.inputmethod.InputContentInfo object. 209 * 210 * <p>This method always returns {@code null} on API <= 24.</p> 211 * 212 * @return an equivalent android.view.inputmethod.InputContentInfo object, or {@code null} if 213 * not supported. 214 */ unwrap()215 public @Nullable Object unwrap() { 216 return mImpl.getInputContentInfo(); 217 } 218 219 /** 220 * Requests a temporary read-only access permission for content URI associated with this object. 221 * 222 * <p>The lifecycle of the permission granted here is tied to this object instance. If the 223 * permission is not released explicitly via {@link #releasePermission()}, it will be 224 * released automatically when there are no more references to this object.</p> 225 * 226 * <p>Does nothing if the temporary permission is already granted.</p> 227 */ requestPermission()228 public void requestPermission() { 229 mImpl.requestPermission(); 230 } 231 232 /** 233 * Releases a temporary read-only access permission for content URI associated with this object. 234 * 235 * <p>Does nothing if the temporary permission is not granted.</p> 236 */ releasePermission()237 public void releasePermission() { 238 mImpl.releasePermission(); 239 } 240 } 241