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 android.support.v13.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; 25import android.support.annotation.NonNull; 26import android.support.v4.os.BuildCompat; 27 28import java.util.Arrays; 29 30/** 31 * Helper for accessing features in {@link Fragment} introduced after 32 * API level 13 in a backwards compatible fashion. 33 */ 34public class FragmentCompat { 35 interface FragmentCompatImpl { 36 void setMenuVisibility(Fragment f, boolean visible); 37 void setUserVisibleHint(Fragment f, boolean deferStart); 38 void requestPermissions(Fragment fragment, String[] permissions, int requestCode); 39 boolean shouldShowRequestPermissionRationale(Fragment fragment, String permission); 40 } 41 42 static class BaseFragmentCompatImpl implements FragmentCompatImpl { 43 public void setMenuVisibility(Fragment f, boolean visible) { 44 } 45 public void setUserVisibleHint(Fragment f, boolean deferStart) { 46 } 47 public void requestPermissions(final Fragment fragment, final String[] permissions, 48 final int requestCode) { 49 Handler handler = new Handler(Looper.getMainLooper()); 50 handler.post(new Runnable() { 51 @Override 52 public void run() { 53 final int[] grantResults = new int[permissions.length]; 54 55 Context context = fragment.getActivity(); 56 if (context != null) { 57 PackageManager packageManager = context.getPackageManager(); 58 String packageName = context.getPackageName(); 59 60 final int permissionCount = permissions.length; 61 for (int i = 0; i < permissionCount; i++) { 62 grantResults[i] = packageManager.checkPermission( 63 permissions[i], packageName); 64 } 65 } else { 66 Arrays.fill(grantResults, PackageManager.PERMISSION_DENIED); 67 } 68 69 ((OnRequestPermissionsResultCallback) fragment).onRequestPermissionsResult( 70 requestCode, permissions, grantResults); 71 } 72 }); 73 } 74 public boolean shouldShowRequestPermissionRationale(Fragment fragment, String permission) { 75 return false; 76 } 77 } 78 79 static class ICSFragmentCompatImpl extends BaseFragmentCompatImpl { 80 @Override 81 public void setMenuVisibility(Fragment f, boolean visible) { 82 FragmentCompatICS.setMenuVisibility(f, visible); 83 } 84 } 85 86 static class ICSMR1FragmentCompatImpl extends ICSFragmentCompatImpl { 87 @Override 88 public void setUserVisibleHint(Fragment f, boolean deferStart) { 89 FragmentCompatICSMR1.setUserVisibleHint(f, deferStart); 90 } 91 } 92 93 static class MncFragmentCompatImpl extends ICSMR1FragmentCompatImpl { 94 @Override 95 public void requestPermissions(Fragment fragment, String[] permissions, int requestCode) { 96 FragmentCompat23.requestPermissions(fragment, permissions, requestCode); 97 } 98 99 @Override 100 public boolean shouldShowRequestPermissionRationale(Fragment fragment, String permission) { 101 return FragmentCompat23.shouldShowRequestPermissionRationale(fragment, permission); 102 } 103 } 104 105 static class NFragmentCompatImpl extends MncFragmentCompatImpl { 106 @Override 107 public void setUserVisibleHint(Fragment f, boolean deferStart) { 108 FragmentCompatApi24.setUserVisibleHint(f, deferStart); 109 } 110 } 111 112 static final FragmentCompatImpl IMPL; 113 static { 114 if (BuildCompat.isAtLeastN()) { 115 IMPL = new NFragmentCompatImpl(); 116 } else if (Build.VERSION.SDK_INT >= 23) { 117 IMPL = new MncFragmentCompatImpl(); 118 } else if (android.os.Build.VERSION.SDK_INT >= 15) { 119 IMPL = new ICSMR1FragmentCompatImpl(); 120 } else if (android.os.Build.VERSION.SDK_INT >= 14) { 121 IMPL = new ICSFragmentCompatImpl(); 122 } else { 123 IMPL = new BaseFragmentCompatImpl(); 124 } 125 } 126 127 /** 128 * This interface is the contract for receiving the results for permission requests. 129 */ 130 public interface OnRequestPermissionsResultCallback { 131 132 /** 133 * Callback for the result from requesting permissions. This method 134 * is invoked for every call on {@link #requestPermissions(android.app.Fragment, 135 * String[], int)} 136 * 137 * @param requestCode The request code passed in {@link #requestPermissions( 138 * android.app.Fragment, String[], int)} 139 * @param permissions The requested permissions. Never null. 140 * @param grantResults The grant results for the corresponding permissions 141 * which is either {@link android.content.pm.PackageManager#PERMISSION_GRANTED} 142 * or {@link android.content.pm.PackageManager#PERMISSION_DENIED}. Never null. 143 * 144 * @see #requestPermissions(android.app.Fragment, String[], int) 145 */ 146 public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, 147 @NonNull int[] grantResults); 148 } 149 150 /** 151 * Call {@link Fragment#setMenuVisibility(boolean) Fragment.setMenuVisibility(boolean)} 152 * if running on an appropriate version of the platform. 153 */ 154 public static void setMenuVisibility(Fragment f, boolean visible) { 155 IMPL.setMenuVisibility(f, visible); 156 } 157 158 /** 159 * Call {@link Fragment#setUserVisibleHint(boolean) setUserVisibleHint(boolean)} 160 * if running on an appropriate version of the platform. 161 */ 162 public static void setUserVisibleHint(Fragment f, boolean deferStart) { 163 IMPL.setUserVisibleHint(f, deferStart); 164 } 165 166 /** 167 * Requests permissions to be granted to this application. These permissions 168 * must be requested in your manifest, they should not be granted to your app, 169 * and they should have protection level {@link android.content.pm.PermissionInfo 170 * #PROTECTION_DANGEROUS dangerous}, regardless whether they are declared by 171 * the platform or a third-party app. 172 * <p> 173 * Normal permissions {@link android.content.pm.PermissionInfo#PROTECTION_NORMAL} 174 * are granted at install time if requested in the manifest. Signature permissions 175 * {@link android.content.pm.PermissionInfo#PROTECTION_SIGNATURE} are granted at 176 * install time if requested in the manifest and the signature of your app matches 177 * the signature of the app declaring the permissions. 178 * </p> 179 * <p> 180 * If your app does not have the requested permissions the user will be presented 181 * with UI for accepting them. After the user has accepted or rejected the 182 * requested permissions you will receive a callback reporting whether the 183 * permissions were granted or not. Your fragment has to implement {@link 184 * OnRequestPermissionsResultCallback} 185 * and the results of permission requests will be delivered to its 186 * {@link OnRequestPermissionsResultCallback#onRequestPermissionsResult( 187 * int, String[], int[])}. 188 * </p> 189 * <p> 190 * Note that requesting a permission does not guarantee it will be granted and 191 * your app should be able to run without having this permission. 192 * </p> 193 * <p> 194 * This method may start an activity allowing the user to choose which permissions 195 * to grant and which to reject. Hence, you should be prepared that your activity 196 * may be paused and resumed. Further, granting some permissions may require 197 * a restart of you application. In such a case, the system will recreate the 198 * activity stack before delivering the result to your onRequestPermissionsResult( 199 * int, String[], int[]). 200 * </p> 201 * <p> 202 * When checking whether you have a permission you should use {@link 203 * android.support.v4.content.ContextCompat#checkSelfPermission( 204 * android.content.Context, String)}. 205 * </p> 206 * 207 * @param fragment The target fragment. 208 * @param permissions The requested permissions. 209 * @param requestCode Application specific request code to match with a result 210 * reported to {@link OnRequestPermissionsResultCallback#onRequestPermissionsResult( 211 * int, String[], int[])}. 212 * 213 * @see android.support.v4.content.ContextCompat#checkSelfPermission( 214 * android.content.Context, String) 215 * @see #shouldShowRequestPermissionRationale(android.app.Fragment, String) 216 */ 217 public static void requestPermissions(@NonNull Fragment fragment, 218 @NonNull String[] permissions, int requestCode) { 219 IMPL.requestPermissions(fragment, permissions, requestCode); 220 } 221 222 /** 223 * Gets whether you should show UI with rationale for requesting a permission. 224 * You should do this only if you do not have the permission and the context in 225 * which the permission is requested does not clearly communicate to the user 226 * what would be the benefit from granting this permission. 227 * <p> 228 * For example, if you write a camera app, requesting the camera permission 229 * would be expected by the user and no rationale for why it is requested is 230 * needed. If however, the app needs location for tagging photos then a non-tech 231 * savvy user may wonder how location is related to taking photos. In this case 232 * you may choose to show UI with rationale of requesting this permission. 233 * </p> 234 * 235 * @param fragment The target fragment. 236 * @param permission A permission your app wants to request. 237 * @return Whether you can show permission rationale UI. 238 * 239 * @see android.support.v4.content.ContextCompat#checkSelfPermission( 240 * android.content.Context, String) 241 * @see #requestPermissions(android.app.Fragment, String[], int) 242 */ 243 public static boolean shouldShowRequestPermissionRationale(@NonNull Fragment fragment, 244 @NonNull String permission) { 245 return IMPL.shouldShowRequestPermissionRationale(fragment, permission); 246 } 247} 248