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.v4.app; 18 19import android.app.Activity; 20import android.content.Context; 21import android.content.Intent; 22import android.content.IntentSender; 23import android.content.pm.PackageManager; 24import android.graphics.Matrix; 25import android.graphics.RectF; 26import android.net.Uri; 27import android.os.Build; 28import android.os.Bundle; 29import android.os.Handler; 30import android.os.Looper; 31import android.os.Parcelable; 32import android.support.annotation.NonNull; 33import android.support.annotation.Nullable; 34import android.support.v4.content.ContextCompat; 35import android.view.View; 36 37import java.util.Arrays; 38import java.util.List; 39import java.util.Map; 40 41/** 42 * Helper for accessing features in {@link android.app.Activity} 43 * introduced after API level 4 in a backwards compatible fashion. 44 */ 45public class ActivityCompat extends ContextCompat { 46 47 /** 48 * This interface is the contract for receiving the results for permission requests. 49 */ 50 public interface OnRequestPermissionsResultCallback { 51 52 /** 53 * Callback for the result from requesting permissions. This method 54 * is invoked for every call on {@link #requestPermissions(android.app.Activity, 55 * String[], int)}. 56 * <p> 57 * <strong>Note:</strong> It is possible that the permissions request interaction 58 * with the user is interrupted. In this case you will receive empty permissions 59 * and results arrays which should be treated as a cancellation. 60 * </p> 61 * 62 * @param requestCode The request code passed in {@link #requestPermissions( 63 * android.app.Activity, String[], int)} 64 * @param permissions The requested permissions. Never null. 65 * @param grantResults The grant results for the corresponding permissions 66 * which is either {@link android.content.pm.PackageManager#PERMISSION_GRANTED} 67 * or {@link android.content.pm.PackageManager#PERMISSION_DENIED}. Never null. 68 * 69 * @see #requestPermissions(android.app.Activity, String[], int) 70 */ 71 public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, 72 @NonNull int[] grantResults); 73 } 74 75 /** 76 * Invalidate the activity's options menu, if able. 77 * 78 * <p>Before API level 11 (Android 3.0/Honeycomb) the lifecycle of the 79 * options menu was controlled primarily by the user's operation of 80 * the hardware menu key. When the user presses down on the menu key 81 * for the first time the menu was created and prepared by calls 82 * to {@link Activity#onCreateOptionsMenu(android.view.Menu)} and 83 * {@link Activity#onPrepareOptionsMenu(android.view.Menu)} respectively. 84 * Subsequent presses of the menu key kept the existing instance of the 85 * Menu itself and called {@link Activity#onPrepareOptionsMenu(android.view.Menu)} 86 * to give the activity an opportunity to contextually alter the menu 87 * before the menu panel was shown.</p> 88 * 89 * <p>In Android 3.0+ the Action Bar forces the options menu to be built early 90 * so that items chosen to show as actions may be displayed when the activity 91 * first becomes visible. The Activity method invalidateOptionsMenu forces 92 * the entire menu to be destroyed and recreated from 93 * {@link Activity#onCreateOptionsMenu(android.view.Menu)}, offering a similar 94 * though heavier-weight opportunity to change the menu's contents. Normally 95 * this functionality is used to support a changing configuration of Fragments.</p> 96 * 97 * <p>Applications may use this support helper to signal a significant change in 98 * activity state that should cause the options menu to be rebuilt. If the app 99 * is running on an older platform version that does not support menu invalidation 100 * the app will still receive {@link Activity#onPrepareOptionsMenu(android.view.Menu)} 101 * the next time the user presses the menu key and this method will return false. 102 * If this method returns true the options menu was successfully invalidated.</p> 103 * 104 * @param activity Invalidate the options menu of this activity 105 * @return true if this operation was supported and it completed; false if it was not available. 106 */ 107 public static boolean invalidateOptionsMenu(Activity activity) { 108 if (Build.VERSION.SDK_INT >= 11) { 109 ActivityCompatHoneycomb.invalidateOptionsMenu(activity); 110 return true; 111 } 112 return false; 113 } 114 115 /** 116 * Start an activity with additional launch information, if able. 117 * 118 * <p>In Android 4.1+ additional options were introduced to allow for more 119 * control on activity launch animations. Applications can use this method 120 * along with {@link ActivityOptionsCompat} to use these animations when 121 * available. When run on versions of the platform where this feature does 122 * not exist the activity will be launched normally.</p> 123 * 124 * @param activity Context to launch activity from. 125 * @param intent The description of the activity to start. 126 * @param options Additional options for how the Activity should be started. 127 * May be null if there are no options. See 128 * {@link ActivityOptionsCompat} for how to build the Bundle 129 * supplied here; there are no supported definitions for 130 * building it manually. 131 */ 132 public static void startActivity(Activity activity, Intent intent, @Nullable Bundle options) { 133 if (Build.VERSION.SDK_INT >= 16) { 134 ActivityCompatJB.startActivity(activity, intent, options); 135 } else { 136 activity.startActivity(intent); 137 } 138 } 139 140 /** 141 * Start new activity with options, if able, for which you would like a 142 * result when it finished. 143 * 144 * <p>In Android 4.1+ additional options were introduced to allow for more 145 * control on activity launch animations. Applications can use this method 146 * along with {@link ActivityOptionsCompat} to use these animations when 147 * available. When run on versions of the platform where this feature does 148 * not exist the activity will be launched normally.</p> 149 * 150 * @param activity Origin activity to launch from. 151 * @param intent The description of the activity to start. 152 * @param requestCode If >= 0, this code will be returned in 153 * onActivityResult() when the activity exits. 154 * @param options Additional options for how the Activity should be started. 155 * May be null if there are no options. See 156 * {@link ActivityOptionsCompat} for how to build the Bundle 157 * supplied here; there are no supported definitions for 158 * building it manually. 159 */ 160 public static void startActivityForResult(Activity activity, Intent intent, int requestCode, 161 @Nullable Bundle options) { 162 if (Build.VERSION.SDK_INT >= 16) { 163 ActivityCompatJB.startActivityForResult(activity, intent, requestCode, options); 164 } else { 165 activity.startActivityForResult(intent, requestCode); 166 } 167 } 168 169 /** 170 * Start new IntentSender with options, if able, for which you would like a 171 * result when it finished. 172 * 173 * <p>In Android 4.1+ additional options were introduced to allow for more 174 * control on activity launch animations. Applications can use this method 175 * along with {@link ActivityOptionsCompat} to use these animations when 176 * available. When run on versions of the platform where this feature does 177 * not exist the activity will be launched normally.</p> 178 * 179 * @param activity Origin activity to launch from. 180 * @param intent The IntentSender to launch. 181 * @param requestCode If >= 0, this code will be returned in 182 * onActivityResult() when the activity exits. 183 * @param fillInIntent If non-null, this will be provided as the 184 * intent parameter to {@link IntentSender#sendIntent}. 185 * @param flagsMask Intent flags in the original IntentSender that you 186 * would like to change. 187 * @param flagsValues Desired values for any bits set in <var>flagsMask</var> 188 * @param extraFlags Always set to 0. 189 * @param options Additional options for how the Activity should be started. 190 * May be null if there are no options. See 191 * {@link ActivityOptionsCompat} for how to build the Bundle 192 * supplied here; there are no supported definitions for 193 * building it manually. 194 */ 195 public static void startIntentSenderForResult(Activity activity, IntentSender intent, 196 int requestCode, Intent fillInIntent, int flagsMask, int flagsValues, 197 int extraFlags, @Nullable Bundle options) throws IntentSender.SendIntentException { 198 if (Build.VERSION.SDK_INT >= 16) { 199 ActivityCompatJB.startIntentSenderForResult(activity, intent, requestCode, fillInIntent, 200 flagsMask, flagsValues, extraFlags, options); 201 } else if (Build.VERSION.SDK_INT >= 5) { 202 ActivityCompatEclair.startIntentSenderForResult(activity, intent, requestCode, 203 fillInIntent, flagsMask, flagsValues, extraFlags); 204 } 205 } 206 207 /** 208 * Finish this activity, and tries to finish all activities immediately below it 209 * in the current task that have the same affinity. 210 * 211 * <p>On Android 4.1+ calling this method will call through to the native version of this 212 * method. For other platforms {@link Activity#finish()} will be called instead.</p> 213 */ 214 public static void finishAffinity(Activity activity) { 215 if (Build.VERSION.SDK_INT >= 16) { 216 ActivityCompatJB.finishAffinity(activity); 217 } else { 218 activity.finish(); 219 } 220 } 221 222 /** 223 * Reverses the Activity Scene entry Transition and triggers the calling Activity 224 * to reverse its exit Transition. When the exit Transition completes, 225 * {@link Activity#finish()} is called. If no entry Transition was used, finish() is called 226 * immediately and the Activity exit Transition is run. 227 * 228 * <p>On Android 4.4 or lower, this method only finishes the Activity with no 229 * special exit transition.</p> 230 */ 231 public static void finishAfterTransition(Activity activity) { 232 if (Build.VERSION.SDK_INT >= 21) { 233 ActivityCompat21.finishAfterTransition(activity); 234 } else { 235 activity.finish(); 236 } 237 } 238 239 /** 240 * Backwards compatible implementation of {@link android.app.Activity#getReferrer() 241 * Activity.getReferrer}. Uses the platform's implementation if available, otherwise 242 * only falls back to digging any explicitly specified referrer from the activity's intent. 243 */ 244 public Uri getReferrer(Activity activity) { 245 if (Build.VERSION.SDK_INT >= 22) { 246 return ActivityCompat22.getReferrer(activity); 247 } 248 Intent intent = activity.getIntent(); 249 Uri referrer = intent.getParcelableExtra("android.intent.extra.REFERRER"); 250 if (referrer != null) { 251 return referrer; 252 } 253 String referrerName = intent.getStringExtra("android.intent.extra.REFERRER_NAME"); 254 if (referrerName != null) { 255 return Uri.parse(referrerName); 256 } 257 return null; 258 } 259 260 /** 261 * When {@link android.app.ActivityOptions#makeSceneTransitionAnimation(Activity, 262 * android.view.View, String)} was used to start an Activity, <var>callback</var> 263 * will be called to handle shared elements on the <i>launched</i> Activity. This requires 264 * {@link android.view.Window#FEATURE_CONTENT_TRANSITIONS}. 265 * 266 * @param callback Used to manipulate shared element transitions on the launched Activity. 267 */ 268 public static void setEnterSharedElementCallback(Activity activity, 269 SharedElementCallback callback) { 270 if (Build.VERSION.SDK_INT >= 21) { 271 ActivityCompat21.setEnterSharedElementCallback(activity, createCallback(callback)); 272 } 273 } 274 275 /** 276 * When {@link android.app.ActivityOptions#makeSceneTransitionAnimation(Activity, 277 * android.view.View, String)} was used to start an Activity, <var>callback</var> 278 * will be called to handle shared elements on the <i>launching</i> Activity. Most 279 * calls will only come when returning from the started Activity. 280 * This requires {@link android.view.Window#FEATURE_CONTENT_TRANSITIONS}. 281 * 282 * @param callback Used to manipulate shared element transitions on the launching Activity. 283 */ 284 public static void setExitSharedElementCallback(Activity activity, 285 SharedElementCallback callback) { 286 if (Build.VERSION.SDK_INT >= 21) { 287 ActivityCompat21.setExitSharedElementCallback(activity, createCallback(callback)); 288 } 289 } 290 291 public static void postponeEnterTransition(Activity activity) { 292 if (Build.VERSION.SDK_INT >= 21) { 293 ActivityCompat21.postponeEnterTransition(activity); 294 } 295 } 296 297 public static void startPostponedEnterTransition(Activity activity) { 298 if (Build.VERSION.SDK_INT >= 21) { 299 ActivityCompat21.startPostponedEnterTransition(activity); 300 } 301 } 302 303 /** 304 * Requests permissions to be granted to this application. These permissions 305 * must be requested in your manifest, they should not be granted to your app, 306 * and they should have protection level {@link android.content.pm.PermissionInfo 307 * #PROTECTION_DANGEROUS dangerous}, regardless whether they are declared by 308 * the platform or a third-party app. 309 * <p> 310 * Normal permissions {@link android.content.pm.PermissionInfo#PROTECTION_NORMAL} 311 * are granted at install time if requested in the manifest. Signature permissions 312 * {@link android.content.pm.PermissionInfo#PROTECTION_SIGNATURE} are granted at 313 * install time if requested in the manifest and the signature of your app matches 314 * the signature of the app declaring the permissions. 315 * </p> 316 * <p> 317 * If your app does not have the requested permissions the user will be presented 318 * with UI for accepting them. After the user has accepted or rejected the 319 * requested permissions you will receive a callback reporting whether the 320 * permissions were granted or not. Your activity has to implement {@link 321 * android.support.v4.app.ActivityCompat.OnRequestPermissionsResultCallback} 322 * and the results of permission requests will be delivered to its {@link 323 * android.support.v4.app.ActivityCompat.OnRequestPermissionsResultCallback#onRequestPermissionsResult( 324 * int, String[], int[])} method. 325 * </p> 326 * <p> 327 * Note that requesting a permission does not guarantee it will be granted and 328 * your app should be able to run without having this permission. 329 * </p> 330 * <p> 331 * This method may start an activity allowing the user to choose which permissions 332 * to grant and which to reject. Hence, you should be prepared that your activity 333 * may be paused and resumed. Further, granting some permissions may require 334 * a restart of you application. In such a case, the system will recreate the 335 * activity stack before delivering the result to your onRequestPermissionsResult( 336 * int, String[], int[]). 337 * </p> 338 * <p> 339 * When checking whether you have a permission you should use {@link 340 * #checkSelfPermission(android.content.Context, String)}. 341 * </p> 342 * <p> 343 * Calling this API for permissions already granted to your app would show UI 344 * to the user to decided whether the app can still hold these permissions. This 345 * can be useful if the way your app uses the data guarded by the permissions 346 * changes significantly. 347 * </p> 348 * 349 * @param activity The target activity. 350 * @param permissions The requested permissions. 351 * @param requestCode Application specific request code to match with a result 352 * reported to {@link OnRequestPermissionsResultCallback#onRequestPermissionsResult( 353 * int, String[], int[])}. 354 * 355 * @see #checkSelfPermission(android.content.Context, String) 356 * @see #shouldShowRequestPermissionRationale(android.app.Activity, String) 357 */ 358 public static void requestPermissions(final @NonNull Activity activity, 359 final @NonNull String[] permissions, final int requestCode) { 360 if (Build.VERSION.SDK_INT >= 23) { 361 ActivityCompatApi23.requestPermissions(activity, permissions, requestCode); 362 } else if (activity instanceof OnRequestPermissionsResultCallback) { 363 Handler handler = new Handler(Looper.getMainLooper()); 364 handler.post(new Runnable() { 365 @Override 366 public void run() { 367 final int[] grantResults = new int[permissions.length]; 368 369 PackageManager packageManager = activity.getPackageManager(); 370 String packageName = activity.getPackageName(); 371 372 final int permissionCount = permissions.length; 373 for (int i = 0; i < permissionCount; i++) { 374 grantResults[i] = packageManager.checkPermission( 375 permissions[i], packageName); 376 } 377 378 ((OnRequestPermissionsResultCallback) activity).onRequestPermissionsResult( 379 requestCode, permissions, grantResults); 380 } 381 }); 382 } 383 } 384 385 /** 386 * Gets whether you should show UI with rationale for requesting a permission. 387 * You should do this only if you do not have the permission and the context in 388 * which the permission is requested does not clearly communicate to the user 389 * what would be the benefit from granting this permission. 390 * <p> 391 * For example, if you write a camera app, requesting the camera permission 392 * would be expected by the user and no rationale for why it is requested is 393 * needed. If however, the app needs location for tagging photos then a non-tech 394 * savvy user may wonder how location is related to taking photos. In this case 395 * you may choose to show UI with rationale of requesting this permission. 396 * </p> 397 * 398 * @param activity The target activity. 399 * @param permission A permission your app wants to request. 400 * @return Whether you can show permission rationale UI. 401 * 402 * @see #checkSelfPermission(android.content.Context, String) 403 * @see #requestPermissions(android.app.Activity, String[], int) 404 */ 405 public static boolean shouldShowRequestPermissionRationale(@NonNull Activity activity, 406 @NonNull String permission) { 407 if (Build.VERSION.SDK_INT >= 23) { 408 return ActivityCompatApi23.shouldShowRequestPermissionRationale(activity, permission); 409 } 410 return false; 411 } 412 413 private static ActivityCompat21.SharedElementCallback21 createCallback( 414 SharedElementCallback callback) { 415 ActivityCompat21.SharedElementCallback21 newCallback = null; 416 if (callback != null) { 417 newCallback = new ActivityCompat.SharedElementCallback21Impl(callback); 418 } 419 return newCallback; 420 } 421 422 private static class SharedElementCallback21Impl 423 extends ActivityCompat21.SharedElementCallback21 { 424 425 private SharedElementCallback mCallback; 426 427 public SharedElementCallback21Impl(SharedElementCallback callback) { 428 mCallback = callback; 429 } 430 431 @Override 432 public void onSharedElementStart(List<String> sharedElementNames, 433 List<View> sharedElements, List<View> sharedElementSnapshots) { 434 mCallback.onSharedElementStart(sharedElementNames, sharedElements, 435 sharedElementSnapshots); 436 } 437 438 @Override 439 public void onSharedElementEnd(List<String> sharedElementNames, List<View> sharedElements, 440 List<View> sharedElementSnapshots) { 441 mCallback.onSharedElementEnd(sharedElementNames, sharedElements, 442 sharedElementSnapshots); 443 } 444 445 @Override 446 public void onRejectSharedElements(List<View> rejectedSharedElements) { 447 mCallback.onRejectSharedElements(rejectedSharedElements); 448 } 449 450 @Override 451 public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) { 452 mCallback.onMapSharedElements(names, sharedElements); 453 } 454 455 @Override 456 public Parcelable onCaptureSharedElementSnapshot(View sharedElement, 457 Matrix viewToGlobalMatrix, RectF screenBounds) { 458 return mCallback.onCaptureSharedElementSnapshot(sharedElement, viewToGlobalMatrix, 459 screenBounds); 460 } 461 462 @Override 463 public View onCreateSnapshotView(Context context, Parcelable snapshot) { 464 return mCallback.onCreateSnapshotView(context, snapshot); 465 } 466 } 467} 468