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) setParentForTesting(Object parentForTesting)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 getParent(@onNull Fragment fragment, @NonNull Class<T> callbackInterface)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 } else if (fragment.getActivity() instanceof MainActivityPeer.PeerSupplier) { 63 MainActivityPeer peer = ((MainActivityPeer.PeerSupplier) fragment.getActivity()).getPeer(); 64 if (peer instanceof FragmentUtilListener) { 65 return ((FragmentUtilListener) peer).getImpl(callbackInterface); 66 } 67 } 68 return null; 69 } 70 71 /** 72 * Returns an instance of the {@code callbackInterface} that is defined in the parent of the 73 * {@code fragment}, or null if no such call back can be found. 74 */ 75 @CheckResult(suggest = "#checkParent(Fragment, Class)}") 76 @Nullable getParent( @onNull android.app.Fragment fragment, @NonNull Class<T> callbackInterface)77 public static <T> T getParent( 78 @NonNull android.app.Fragment fragment, @NonNull Class<T> callbackInterface) { 79 if (callbackInterface.isInstance(parentForTesting)) { 80 @SuppressWarnings("unchecked") // Casts are checked using runtime methods 81 T parent = (T) parentForTesting; 82 return parent; 83 } 84 85 android.app.Fragment parentFragment = fragment.getParentFragment(); 86 if (callbackInterface.isInstance(parentFragment)) { 87 @SuppressWarnings("unchecked") // Casts are checked using runtime methods 88 T parent = (T) parentFragment; 89 return parent; 90 } else if (callbackInterface.isInstance(fragment.getActivity())) { 91 @SuppressWarnings("unchecked") // Casts are checked using runtime methods 92 T parent = (T) fragment.getActivity(); 93 return parent; 94 } else if (fragment.getActivity() instanceof FragmentUtilListener) { 95 @SuppressWarnings("unchecked") // Casts are checked using runtime methods 96 T parent = ((FragmentUtilListener) fragment.getActivity()).getImpl(callbackInterface); 97 return parent; 98 } else if (fragment.getActivity() instanceof MainActivityPeer.PeerSupplier) { 99 MainActivityPeer peer = ((MainActivityPeer.PeerSupplier) fragment.getActivity()).getPeer(); 100 if (peer instanceof FragmentUtilListener) { 101 return ((FragmentUtilListener) peer).getImpl(callbackInterface); 102 } 103 } 104 return null; 105 } 106 107 /** Returns the parent or throws. Should perform check elsewhere(e.g. onAttach, newInstance). */ 108 @NonNull getParentUnsafe( @onNull Fragment fragment, @NonNull Class<T> callbackInterface)109 public static <T> T getParentUnsafe( 110 @NonNull Fragment fragment, @NonNull Class<T> callbackInterface) { 111 return Assert.isNotNull(getParent(fragment, callbackInterface)); 112 } 113 114 /** 115 * Version of {@link #getParentUnsafe(Fragment, Class)} which supports {@link 116 * android.app.Fragment}. 117 */ 118 @NonNull getParentUnsafe( @onNull android.app.Fragment fragment, @NonNull Class<T> callbackInterface)119 public static <T> T getParentUnsafe( 120 @NonNull android.app.Fragment fragment, @NonNull Class<T> callbackInterface) { 121 return Assert.isNotNull(getParent(fragment, callbackInterface)); 122 } 123 124 /** 125 * Ensures fragment has a parent that implements the corresponding interface 126 * 127 * @param frag The Fragment whose parents are to be checked 128 * @param callbackInterface The interface class that a parent should implement 129 * @throws IllegalStateException if no parents are found that implement callbackInterface 130 */ checkParent(@onNull Fragment frag, @NonNull Class<?> callbackInterface)131 public static void checkParent(@NonNull Fragment frag, @NonNull Class<?> callbackInterface) 132 throws IllegalStateException { 133 if (parentForTesting != null) { 134 return; 135 } 136 if (FragmentUtils.getParent(frag, callbackInterface) == null) { 137 String parent = 138 frag.getParentFragment() == null 139 ? frag.getActivity().getClass().getName() 140 : frag.getParentFragment().getClass().getName(); 141 throw new IllegalStateException( 142 frag.getClass().getName() 143 + " must be added to a parent" 144 + " that implements " 145 + callbackInterface.getName() 146 + ". Instead found " 147 + parent); 148 } 149 } 150 151 /** Useful interface for activities that don't want to implement arbitrary listeners. */ 152 public interface FragmentUtilListener { 153 154 /** Returns an implementation of T if parent has one, otherwise null. */ 155 @Nullable getImpl(Class<T> callbackInterface)156 <T> T getImpl(Class<T> callbackInterface); 157 } 158 } 159