1/* 2 * Copyright (C) 2013 Google Inc. 3 * Licensed to The Android Open Source Project. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18package com.android.mail.ui; 19 20import android.animation.Animator; 21import android.animation.AnimatorInflater; 22import android.animation.AnimatorListenerAdapter; 23import android.app.Fragment; 24import android.content.res.Resources; 25import android.os.Handler; 26import android.view.View; 27 28import com.android.mail.R; 29import com.android.mail.utils.Utils; 30 31/** 32 * <p>Controller to handle showing and hiding progress in the conversation views.</p> 33 * <br> 34 * <b>NOTE:</b> This class makes several assumptions about the view hierarchy 35 * of the conversation view. Use with care. 36 */ 37public class ConversationViewProgressController { 38 private static int sMinDelay = -1; 39 private static int sMinShowTime = -1; 40 41 private final Handler mHandler; 42 43 private final Fragment mFragment; 44 45 private long mLoadingShownTime = -1; 46 private View mProgressView; 47 private View mBackgroundView; 48 49 private final Runnable mDelayedShow; 50 51 public ConversationViewProgressController(Fragment fragment, Handler handler) { 52 mFragment = fragment; 53 mHandler = handler; 54 55 mDelayedShow = new FragmentRunnable("mDelayedShow", mFragment) { 56 @Override 57 public void go() { 58 mLoadingShownTime = System.currentTimeMillis(); 59 mProgressView.setVisibility(View.VISIBLE); 60 } 61 }; 62 } 63 64 public void instantiateProgressIndicators(View rootView) { 65 mBackgroundView = rootView.findViewById(R.id.background_view); 66 mProgressView = rootView.findViewById(R.id.loading_progress); 67 } 68 69 public void showLoadingStatus(boolean isFragmentUserVisible) { 70 if (!isFragmentUserVisible) { 71 return; 72 } 73 if (sMinDelay == -1) { 74 Resources res = mFragment.getResources(); 75 sMinDelay = res.getInteger(R.integer.conversationview_show_loading_delay); 76 sMinShowTime = res.getInteger(R.integer.conversationview_min_show_loading); 77 } 78 // If the loading view isn't already showing, show it and remove any 79 // pending calls to show the loading screen. 80 mBackgroundView.setVisibility(View.VISIBLE); 81 mHandler.removeCallbacks(mDelayedShow); 82 mHandler.postDelayed(mDelayedShow, sMinDelay); 83 } 84 85 public void dismissLoadingStatus() { 86 dismissLoadingStatus(null); 87 } 88 89 /** 90 * Begin the fade-out animation to hide the Progress overlay, either immediately or after some 91 * timeout (to ensure that the progress minimum time elapses). 92 * 93 * @param doAfter an optional Runnable action to execute after the animation completes 94 */ 95 public void dismissLoadingStatus(final Runnable doAfter) { 96 if (mLoadingShownTime == -1) { 97 // The runnable hasn't run yet, so just remove it. 98 mHandler.removeCallbacks(mDelayedShow); 99 dismiss(doAfter); 100 return; 101 } 102 final long diff = Math.abs(System.currentTimeMillis() - mLoadingShownTime); 103 if (diff > sMinShowTime) { 104 dismiss(doAfter); 105 } else { 106 mHandler.postDelayed(new FragmentRunnable("dismissLoadingStatus", mFragment) { 107 @Override 108 public void go() { 109 dismiss(doAfter); 110 } 111 }, Math.abs(sMinShowTime - diff)); 112 } 113 } 114 115 private void dismiss(final Runnable doAfter) { 116 // Reset loading shown time. 117 mLoadingShownTime = -1; 118 mProgressView.setVisibility(View.GONE); 119 if (mBackgroundView.getVisibility() == View.VISIBLE) { 120 animateDismiss(doAfter); 121 } else { 122 if (doAfter != null) { 123 doAfter.run(); 124 } 125 } 126 } 127 128 private void animateDismiss(final Runnable doAfter) { 129 // the animation can only work (and is only worth doing) if this fragment is added 130 // reasons it may not be added: fragment is being destroyed, or in the process of being 131 // restored 132 if (!mFragment.isAdded()) { 133 mBackgroundView.setVisibility(View.GONE); 134 return; 135 } 136 137 Utils.enableHardwareLayer(mBackgroundView); 138 final Animator animator = AnimatorInflater.loadAnimator( 139 mFragment.getActivity().getApplicationContext(), R.anim.fade_out); 140 animator.setTarget(mBackgroundView); 141 animator.addListener(new AnimatorListenerAdapter() { 142 @Override 143 public void onAnimationEnd(Animator animation) { 144 mBackgroundView.setVisibility(View.GONE); 145 mBackgroundView.setLayerType(View.LAYER_TYPE_NONE, null); 146 if (doAfter != null) { 147 doAfter.run(); 148 } 149 } 150 }); 151 animator.start(); 152 } 153} 154