1/* 2 * Copyright (C) 2011 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 17package androidx.legacy.app; 18 19import android.app.Fragment; 20import android.content.Context; 21import android.content.pm.PackageManager; 22import android.os.Build; 23import android.os.Handler; 24import android.os.Looper; 25 26import androidx.annotation.NonNull; 27import androidx.annotation.RequiresApi; 28import androidx.annotation.RestrictTo; 29 30import java.util.Arrays; 31 32/** 33 * Helper for accessing features in {@link Fragment} in a backwards compatible fashion. 34 * 35 * @deprecated Use {@link androidx.fragment.app.Fragment} instead of the framework fragment. 36 */ 37@Deprecated 38public class FragmentCompat { 39 40 /** 41 * @deprecated Use {@link androidx.fragment.app.Fragment} instead of the framework fragment. 42 */ 43 @Deprecated 44 public FragmentCompat() { 45 } 46 47 interface FragmentCompatImpl { 48 void setUserVisibleHint(Fragment f, boolean deferStart); 49 void requestPermissions(Fragment fragment, String[] permissions, int requestCode); 50 boolean shouldShowRequestPermissionRationale(Fragment fragment, String permission); 51 } 52 53 /** 54 * Customizable delegate that allows delegating permission related compatibility methods 55 * to a custom implementation. 56 * 57 * <p> 58 * To delegate fragment compatibility methods to a custom class, implement this interface, 59 * and call {@code FragmentCompat.setPermissionCompatDelegate(delegate);}. All future calls 60 * to the compatibility methods in this class will first check whether the delegate can 61 * handle the method call, and invoke the corresponding method if it can. 62 * </p> 63 * 64 * @deprecated Use {@link androidx.fragment.app.Fragment} instead of the framework 65 * {@link Fragment}. 66 */ 67 @Deprecated 68 public interface PermissionCompatDelegate { 69 70 /** 71 * Determines whether the delegate should handle 72 * {@link FragmentCompat#requestPermissions(Fragment, String[], int)}, and request 73 * permissions if applicable. If this method returns true, it means that permission 74 * request is successfully handled by the delegate, and platform should not perform any 75 * further requests for permission. 76 * 77 * @param fragment The target fragment. 78 * @param permissions The requested permissions. 79 * @param requestCode Application specific request code to match with a result 80 * reported to {@link OnRequestPermissionsResultCallback#onRequestPermissionsResult( 81 * int, String[], int[])}. 82 * 83 * @return Whether the delegate has handled the permission request. 84 * @see FragmentCompat#requestPermissions(Fragment, String[], int) 85 * 86 * @deprecated Use {@link androidx.fragment.app.Fragment} instead of the framework 87 * {@link Fragment}. 88 */ 89 @Deprecated 90 boolean requestPermissions(Fragment fragment, String[] permissions, int requestCode); 91 } 92 93 static class FragmentCompatBaseImpl implements FragmentCompatImpl { 94 @Override 95 public void setUserVisibleHint(Fragment f, boolean deferStart) { 96 } 97 @Override 98 public void requestPermissions(final Fragment fragment, final String[] permissions, 99 final int requestCode) { 100 Handler handler = new Handler(Looper.getMainLooper()); 101 handler.post(new Runnable() { 102 @Override 103 public void run() { 104 final int[] grantResults = new int[permissions.length]; 105 106 Context context = fragment.getActivity(); 107 if (context != null) { 108 PackageManager packageManager = context.getPackageManager(); 109 String packageName = context.getPackageName(); 110 111 final int permissionCount = permissions.length; 112 for (int i = 0; i < permissionCount; i++) { 113 grantResults[i] = packageManager.checkPermission( 114 permissions[i], packageName); 115 } 116 } else { 117 Arrays.fill(grantResults, PackageManager.PERMISSION_DENIED); 118 } 119 120 ((OnRequestPermissionsResultCallback) fragment).onRequestPermissionsResult( 121 requestCode, permissions, grantResults); 122 } 123 }); 124 } 125 @Override 126 public boolean shouldShowRequestPermissionRationale(Fragment fragment, String permission) { 127 return false; 128 } 129 } 130 131 @RequiresApi(15) 132 static class FragmentCompatApi15Impl extends FragmentCompatBaseImpl { 133 @Override 134 public void setUserVisibleHint(Fragment f, boolean deferStart) { 135 f.setUserVisibleHint(deferStart); 136 } 137 } 138 139 @RequiresApi(23) 140 static class FragmentCompatApi23Impl extends FragmentCompatApi15Impl { 141 @Override 142 public void requestPermissions(Fragment fragment, String[] permissions, int requestCode) { 143 fragment.requestPermissions(permissions, requestCode); 144 } 145 146 @Override 147 public boolean shouldShowRequestPermissionRationale(Fragment fragment, String permission) { 148 return fragment.shouldShowRequestPermissionRationale(permission); 149 } 150 } 151 152 @RequiresApi(24) 153 static class FragmentCompatApi24Impl extends FragmentCompatApi23Impl { 154 @Override 155 public void setUserVisibleHint(Fragment f, boolean deferStart) { 156 f.setUserVisibleHint(deferStart); 157 } 158 } 159 160 static final FragmentCompatImpl IMPL; 161 static { 162 if (Build.VERSION.SDK_INT >= 24) { 163 IMPL = new FragmentCompatApi24Impl(); 164 } else if (Build.VERSION.SDK_INT >= 23) { 165 IMPL = new FragmentCompatApi23Impl(); 166 } else if (android.os.Build.VERSION.SDK_INT >= 15) { 167 IMPL = new FragmentCompatApi15Impl(); 168 } else { 169 IMPL = new FragmentCompatBaseImpl(); 170 } 171 } 172 173 private static PermissionCompatDelegate sDelegate; 174 175 /** 176 * Sets the permission delegate for {@code FragmentCompat}. Replaces the previously set 177 * delegate. 178 * 179 * @param delegate The delegate to be set. {@code null} to clear the set delegate. 180 * 181 * @deprecated Use {@link androidx.fragment.app.Fragment} instead of the framework 182 * {@link Fragment}. 183 */ 184 @Deprecated 185 public static void setPermissionCompatDelegate(PermissionCompatDelegate delegate) { 186 sDelegate = delegate; 187 } 188 189 /** 190 * @hide 191 * 192 * @deprecated Use {@link androidx.fragment.app.Fragment} instead of the framework 193 * {@link Fragment}. 194 */ 195 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 196 @Deprecated 197 public static PermissionCompatDelegate getPermissionCompatDelegate() { 198 return sDelegate; 199 } 200 201 /** 202 * This interface is the contract for receiving the results for permission requests. 203 * 204 * @deprecated Use {@link androidx.fragment.app.Fragment} instead of the framework 205 * {@link Fragment}. 206 */ 207 @Deprecated 208 public interface OnRequestPermissionsResultCallback { 209 210 /** 211 * Callback for the result from requesting permissions. This method 212 * is invoked for every call on {@link #requestPermissions(android.app.Fragment, 213 * String[], int)} 214 * 215 * @param requestCode The request code passed in {@link #requestPermissions( 216 * android.app.Fragment, String[], int)} 217 * @param permissions The requested permissions. Never null. 218 * @param grantResults The grant results for the corresponding permissions 219 * which is either {@link android.content.pm.PackageManager#PERMISSION_GRANTED} 220 * or {@link android.content.pm.PackageManager#PERMISSION_DENIED}. Never null. 221 * 222 * @see #requestPermissions(android.app.Fragment, String[], int) 223 * 224 * @deprecated Use {@link androidx.fragment.app.Fragment} instead of the framework 225 * {@link Fragment}. 226 */ 227 @Deprecated 228 public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, 229 @NonNull int[] grantResults); 230 } 231 232 /** 233 * Call {@link Fragment#setMenuVisibility(boolean) Fragment.setMenuVisibility(boolean)} 234 * if running on an appropriate version of the platform. 235 * 236 * @deprecated Use {@link androidx.fragment.app.Fragment} instead of the framework 237 * {@link Fragment}. 238 */ 239 @Deprecated 240 public static void setMenuVisibility(Fragment f, boolean visible) { 241 f.setMenuVisibility(visible); 242 } 243 244 /** 245 * Call {@link Fragment#setUserVisibleHint(boolean) setUserVisibleHint(boolean)} 246 * if running on an appropriate version of the platform. 247 * 248 * @deprecated Use {@link androidx.fragment.app.Fragment} instead of the framework 249 * {@link Fragment}. 250 */ 251 @Deprecated 252 public static void setUserVisibleHint(Fragment f, boolean deferStart) { 253 IMPL.setUserVisibleHint(f, deferStart); 254 } 255 256 /** 257 * Requests permissions to be granted to this application. These permissions 258 * must be requested in your manifest, they should not be granted to your app, 259 * and they should have protection level {@link android.content.pm.PermissionInfo 260 * #PROTECTION_DANGEROUS dangerous}, regardless whether they are declared by 261 * the platform or a third-party app. 262 * <p> 263 * Normal permissions {@link android.content.pm.PermissionInfo#PROTECTION_NORMAL} 264 * are granted at install time if requested in the manifest. Signature permissions 265 * {@link android.content.pm.PermissionInfo#PROTECTION_SIGNATURE} are granted at 266 * install time if requested in the manifest and the signature of your app matches 267 * the signature of the app declaring the permissions. 268 * </p> 269 * <p> 270 * If your app does not have the requested permissions the user will be presented 271 * with UI for accepting them. After the user has accepted or rejected the 272 * requested permissions you will receive a callback reporting whether the 273 * permissions were granted or not. Your fragment has to implement {@link 274 * OnRequestPermissionsResultCallback} 275 * and the results of permission requests will be delivered to its 276 * {@link OnRequestPermissionsResultCallback#onRequestPermissionsResult( 277 * int, String[], int[])}. 278 * </p> 279 * <p> 280 * Note that requesting a permission does not guarantee it will be granted and 281 * your app should be able to run without having this permission. 282 * </p> 283 * <p> 284 * This method may start an activity allowing the user to choose which permissions 285 * to grant and which to reject. Hence, you should be prepared that your activity 286 * may be paused and resumed. Further, granting some permissions may require 287 * a restart of you application. In such a case, the system will recreate the 288 * activity stack before delivering the result to your onRequestPermissionsResult( 289 * int, String[], int[]). 290 * </p> 291 * <p> 292 * When checking whether you have a permission you should use {@link 293 * androidx.core.content.ContextCompat#checkSelfPermission( 294 * android.content.Context, String)}. 295 * </p> 296 * 297 * @param fragment The target fragment. 298 * @param permissions The requested permissions. 299 * @param requestCode Application specific request code to match with a result 300 * reported to {@link OnRequestPermissionsResultCallback#onRequestPermissionsResult( 301 * int, String[], int[])}. 302 * 303 * @see androidx.core.content.ContextCompat#checkSelfPermission( 304 * android.content.Context, String) 305 * @see #shouldShowRequestPermissionRationale(android.app.Fragment, String) 306 * 307 * @deprecated Use {@link androidx.fragment.app.Fragment} instead of the framework 308 * {@link Fragment}. 309 */ 310 @Deprecated 311 public static void requestPermissions(@NonNull Fragment fragment, 312 @NonNull String[] permissions, int requestCode) { 313 if (sDelegate != null && sDelegate.requestPermissions(fragment, permissions, requestCode)) { 314 // Delegate has handled the request. 315 return; 316 } 317 318 IMPL.requestPermissions(fragment, permissions, requestCode); 319 } 320 321 /** 322 * Gets whether you should show UI with rationale for requesting a permission. 323 * You should do this only if you do not have the permission and the context in 324 * which the permission is requested does not clearly communicate to the user 325 * what would be the benefit from granting this permission. 326 * <p> 327 * For example, if you write a camera app, requesting the camera permission 328 * would be expected by the user and no rationale for why it is requested is 329 * needed. If however, the app needs location for tagging photos then a non-tech 330 * savvy user may wonder how location is related to taking photos. In this case 331 * you may choose to show UI with rationale of requesting this permission. 332 * </p> 333 * 334 * @param fragment The target fragment. 335 * @param permission A permission your app wants to request. 336 * @return Whether you can show permission rationale UI. 337 * 338 * @see androidx.core.content.ContextCompat#checkSelfPermission( 339 * android.content.Context, String) 340 * @see #requestPermissions(android.app.Fragment, String[], int) 341 * 342 * @deprecated Use {@link androidx.fragment.app.Fragment} instead of the framework 343 * {@link Fragment}. 344 */ 345 @Deprecated 346 public static boolean shouldShowRequestPermissionRationale(@NonNull Fragment fragment, 347 @NonNull String permission) { 348 return IMPL.shouldShowRequestPermissionRationale(fragment, permission); 349 } 350} 351