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