1/* 2 * Copyright (C) 2017 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.transition; 18 19import android.graphics.Matrix; 20import android.support.annotation.NonNull; 21import android.support.annotation.RequiresApi; 22import android.util.Log; 23import android.view.View; 24import android.view.ViewGroup; 25 26import java.lang.reflect.InvocationTargetException; 27import java.lang.reflect.Method; 28 29@RequiresApi(21) 30class GhostViewApi21 implements GhostViewImpl { 31 32 private static final String TAG = "GhostViewApi21"; 33 34 private static Class<?> sGhostViewClass; 35 private static boolean sGhostViewClassFetched; 36 private static Method sAddGhostMethod; 37 private static boolean sAddGhostMethodFetched; 38 private static Method sRemoveGhostMethod; 39 private static boolean sRemoveGhostMethodFetched; 40 41 static class Creator implements GhostViewImpl.Creator { 42 43 @Override 44 public GhostViewImpl addGhost(View view, ViewGroup viewGroup, Matrix matrix) { 45 fetchAddGhostMethod(); 46 if (sAddGhostMethod != null) { 47 try { 48 return new GhostViewApi21( 49 (View) sAddGhostMethod.invoke(null, view, viewGroup, matrix)); 50 } catch (IllegalAccessException e) { 51 // Do nothing 52 } catch (InvocationTargetException e) { 53 throw new RuntimeException(e.getCause()); 54 } 55 } 56 return null; 57 } 58 59 @Override 60 public void removeGhost(View view) { 61 fetchRemoveGhostMethod(); 62 if (sRemoveGhostMethod != null) { 63 try { 64 sRemoveGhostMethod.invoke(null, view); 65 } catch (IllegalAccessException e) { 66 // Do nothing 67 } catch (InvocationTargetException e) { 68 throw new RuntimeException(e.getCause()); 69 } 70 } 71 } 72 73 } 74 75 /** A handle to the platform android.view.GhostView. */ 76 private final View mGhostView; 77 78 private GhostViewApi21(@NonNull View ghostView) { 79 mGhostView = ghostView; 80 } 81 82 @Override 83 public void setVisibility(int visibility) { 84 mGhostView.setVisibility(visibility); 85 } 86 87 @Override 88 public void reserveEndViewTransition(ViewGroup viewGroup, View view) { 89 // No need 90 } 91 92 private static void fetchGhostViewClass() { 93 if (!sGhostViewClassFetched) { 94 try { 95 sGhostViewClass = Class.forName("android.view.GhostView"); 96 } catch (ClassNotFoundException e) { 97 Log.i(TAG, "Failed to retrieve GhostView class", e); 98 } 99 sGhostViewClassFetched = true; 100 } 101 } 102 103 private static void fetchAddGhostMethod() { 104 if (!sAddGhostMethodFetched) { 105 try { 106 fetchGhostViewClass(); 107 sAddGhostMethod = sGhostViewClass.getDeclaredMethod("addGhost", View.class, 108 ViewGroup.class, Matrix.class); 109 sAddGhostMethod.setAccessible(true); 110 } catch (NoSuchMethodException e) { 111 Log.i(TAG, "Failed to retrieve addGhost method", e); 112 } 113 sAddGhostMethodFetched = true; 114 } 115 } 116 117 private static void fetchRemoveGhostMethod() { 118 if (!sRemoveGhostMethodFetched) { 119 try { 120 fetchGhostViewClass(); 121 sRemoveGhostMethod = sGhostViewClass.getDeclaredMethod("removeGhost", View.class); 122 sRemoveGhostMethod.setAccessible(true); 123 } catch (NoSuchMethodException e) { 124 Log.i(TAG, "Failed to retrieve removeGhost method", e); 125 } 126 sRemoveGhostMethodFetched = true; 127 } 128 } 129 130} 131