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 com.android.dialer.common; 18 19 import android.support.annotation.CheckResult; 20 import android.support.annotation.NonNull; 21 import android.support.annotation.Nullable; 22 import android.support.annotation.VisibleForTesting; 23 import android.support.v4.app.Fragment; 24 import com.android.dialer.main.MainActivityPeer; 25 26 /** Utility methods for working with Fragments */ 27 public class FragmentUtils { 28 29 private static Object parentForTesting; 30 31 @VisibleForTesting(otherwise = VisibleForTesting.NONE) 32 public static void setParentForTesting(Object parentForTesting) { 33 FragmentUtils.parentForTesting = parentForTesting; 34 } 35 36 /** 37 * Returns an instance of the {@code callbackInterface} that is defined in the parent of the 38 * {@code fragment}, or null if no such call back can be found. 39 */ 40 @CheckResult(suggest = "#checkParent(Fragment, Class)}") 41 @Nullable 42 public static <T> T getParent(@NonNull Fragment fragment, @NonNull Class<T> callbackInterface) { 43 if (callbackInterface.isInstance(parentForTesting)) { 44 @SuppressWarnings("unchecked") // Casts are checked using runtime methods 45 T parent = (T) parentForTesting; 46 return parent; 47 } 48 49 Fragment parentFragment = fragment.getParentFragment(); 50 if (callbackInterface.isInstance(parentFragment)) { 51 @SuppressWarnings("unchecked") // Casts are checked using runtime methods 52 T parent = (T) parentFragment; 53 return parent; 54 } else if (callbackInterface.isInstance(fragment.getActivity())) { 55 @SuppressWarnings("unchecked") // Casts are checked using runtime methods 56 T parent = (T) fragment.getActivity(); 57 return parent; 58 } else if (fragment.getActivity() instanceof FragmentUtilListener) { 59 @SuppressWarnings("unchecked") // Casts are checked using runtime methods 60 T parent = ((FragmentUtilListener) fragment.getActivity()).getImpl(callbackInterface); 61 return parent; 62 } 63 return null; 64 } 65 66 /** 67 * Returns an instance of the {@code callbackInterface} that is defined in the parent of the 68 * {@code fragment}, or null if no such call back can be found. 69 */ 70 @CheckResult(suggest = "#checkParent(Fragment, Class)}") 71 @Nullable 72 public static <T> T getParent( 73 @NonNull android.app.Fragment fragment, @NonNull Class<T> callbackInterface) { 74 if (callbackInterface.isInstance(parentForTesting)) { 75 @SuppressWarnings("unchecked") // Casts are checked using runtime methods 76 T parent = (T) parentForTesting; 77 return parent; 78 } 79 80 android.app.Fragment parentFragment = fragment.getParentFragment(); 81 if (callbackInterface.isInstance(parentFragment)) { 82 @SuppressWarnings("unchecked") // Casts are checked using runtime methods 83 T parent = (T) parentFragment; 84 return parent; 85 } else if (callbackInterface.isInstance(fragment.getActivity())) { 86 @SuppressWarnings("unchecked") // Casts are checked using runtime methods 87 T parent = (T) fragment.getActivity(); 88 return parent; 89 } else if (fragment.getActivity() instanceof FragmentUtilListener) { 90 @SuppressWarnings("unchecked") // Casts are checked using runtime methods 91 T parent = ((FragmentUtilListener) fragment.getActivity()).getImpl(callbackInterface); 92 return parent; 93 } else if (fragment.getActivity() instanceof MainActivityPeer.PeerSupplier) { 94 MainActivityPeer peer = ((MainActivityPeer.PeerSupplier) fragment.getActivity()).getPeer(); 95 if (peer instanceof FragmentUtilListener) { 96 return ((FragmentUtilListener) peer).getImpl(callbackInterface); 97 } 98 } 99 return null; 100 } 101 102 /** Returns the parent or throws. Should perform check elsewhere(e.g. onAttach, newInstance). */ 103 @NonNull 104 public static <T> T getParentUnsafe( 105 @NonNull Fragment fragment, @NonNull Class<T> callbackInterface) { 106 return Assert.isNotNull(getParent(fragment, callbackInterface)); 107 } 108 109 /** 110 * Version of {@link #getParentUnsafe(Fragment, Class)} which supports {@link 111 * android.app.Fragment}. 112 */ 113 @NonNull 114 public static <T> T getParentUnsafe( 115 @NonNull android.app.Fragment fragment, @NonNull Class<T> callbackInterface) { 116 return Assert.isNotNull(getParent(fragment, callbackInterface)); 117 } 118 119 /** 120 * Ensures fragment has a parent that implements the corresponding interface 121 * 122 * @param frag The Fragment whose parents are to be checked 123 * @param callbackInterface The interface class that a parent should implement 124 * @throws IllegalStateException if no parents are found that implement callbackInterface 125 */ 126 public static void checkParent(@NonNull Fragment frag, @NonNull Class<?> callbackInterface) 127 throws IllegalStateException { 128 if (parentForTesting != null) { 129 return; 130 } 131 if (FragmentUtils.getParent(frag, callbackInterface) == null) { 132 String parent = 133 frag.getParentFragment() == null 134 ? frag.getActivity().getClass().getName() 135 : frag.getParentFragment().getClass().getName(); 136 throw new IllegalStateException( 137 frag.getClass().getName() 138 + " must be added to a parent" 139 + " that implements " 140 + callbackInterface.getName() 141 + ". Instead found " 142 + parent); 143 } 144 } 145 146 /** Useful interface for activities that don't want to implement arbitrary listeners. */ 147 public interface FragmentUtilListener { 148 149 /** Returns an implementation of T if parent has one, otherwise null. */ 150 @Nullable 151 <T> T getImpl(Class<T> callbackInterface); 152 } 153 } 154