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    protected 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    protected 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