Home | History | Annotate | Download | only in common
      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