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 
18 package com.android.mail.ui;
19 
20 import android.animation.Animator;
21 import android.animation.AnimatorInflater;
22 import android.animation.AnimatorListenerAdapter;
23 import android.app.Fragment;
24 import android.content.res.Resources;
25 import android.os.Handler;
26 import android.view.View;
27 
28 import com.android.mail.R;
29 import 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  */
37 public 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 
ConversationViewProgressController(Fragment fragment, Handler handler)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 
instantiateProgressIndicators(View rootView)64     public void instantiateProgressIndicators(View rootView) {
65         mBackgroundView = rootView.findViewById(R.id.background_view);
66         mProgressView = rootView.findViewById(R.id.loading_progress);
67     }
68 
showLoadingStatus(boolean isFragmentUserVisible)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 
dismissLoadingStatus()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      */
dismissLoadingStatus(final Runnable doAfter)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 
dismiss(final Runnable doAfter)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 
animateDismiss(final Runnable doAfter)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