16596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee/*
26596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee * Copyright (C) 2014 The Android Open Source Project
36596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee *
46596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee * Licensed under the Apache License, Version 2.0 (the "License");
56596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee * you may not use this file except in compliance with the License.
66596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee * You may obtain a copy of the License at
76596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee *
86596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee *      http://www.apache.org/licenses/LICENSE-2.0
96596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee *
106596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee * Unless required by applicable law or agreed to in writing, software
116596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee * distributed under the License is distributed on an "AS IS" BASIS,
126596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
136596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee * See the License for the specific language governing permissions and
146596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee * limitations under the License.
156596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee */
166596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee
176596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Leepackage com.android.incallui;
186596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee
196596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Leeimport android.animation.Animator;
206596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Leeimport android.animation.AnimatorListenerAdapter;
216596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Leeimport android.app.Activity;
226596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Leeimport android.app.Fragment;
236596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Leeimport android.app.FragmentManager;
246596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Leeimport android.graphics.Outline;
256596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Leeimport android.graphics.Point;
266596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Leeimport android.os.Bundle;
276596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Leeimport android.view.Display;
286596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Leeimport android.view.LayoutInflater;
296596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Leeimport android.view.View;
306596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Leeimport android.view.ViewAnimationUtils;
316596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Leeimport android.view.ViewGroup;
326596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Leeimport android.view.ViewOutlineProvider;
336596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Leeimport android.view.ViewTreeObserver;
346596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Leeimport android.view.ViewTreeObserver.OnPreDrawListener;
356596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee
366596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Leeimport com.android.contacts.common.util.MaterialColorMapUtils.MaterialPalette;
3768d86c656e5522dec1b04e85ebc0e0f78e436c3dSailesh Nepalimport com.android.dialer.R;
386596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee
396596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Leepublic class CircularRevealFragment extends Fragment {
406596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee    static final String TAG = "CircularRevealFragment";
416596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee
426596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee    private Point mTouchPoint;
436596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee    private OnCircularRevealCompleteListener mListener;
446596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee    private boolean mAnimationStarted;
456596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee
466596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee    interface OnCircularRevealCompleteListener {
476596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee        public void onCircularRevealComplete(FragmentManager fm);
486596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee    }
496596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee
506596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee    public static void startCircularReveal(FragmentManager fm, Point touchPoint,
516596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee            OnCircularRevealCompleteListener listener) {
52d06de8235438ac3b45af381dbf93b83f0fb8460eYorke Lee        if (fm.findFragmentByTag(TAG) == null) {
53d06de8235438ac3b45af381dbf93b83f0fb8460eYorke Lee            fm.beginTransaction().add(R.id.main,
54d06de8235438ac3b45af381dbf93b83f0fb8460eYorke Lee                    new CircularRevealFragment(touchPoint, listener), TAG)
55d06de8235438ac3b45af381dbf93b83f0fb8460eYorke Lee                            .commitAllowingStateLoss();
56d06de8235438ac3b45af381dbf93b83f0fb8460eYorke Lee        } else {
57d06de8235438ac3b45af381dbf93b83f0fb8460eYorke Lee            Log.w(TAG, "An instance of CircularRevealFragment already exists");
58d06de8235438ac3b45af381dbf93b83f0fb8460eYorke Lee        }
596596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee    }
606596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee
616596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee    public static void endCircularReveal(FragmentManager fm) {
626596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee        final Fragment fragment = fm.findFragmentByTag(TAG);
636596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee        if (fragment != null) {
646596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee            fm.beginTransaction().remove(fragment).commitAllowingStateLoss();
656596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee        }
666596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee    }
676596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee
686596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee    /**
696596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee     * Empty constructor used only by the {@link FragmentManager}.
706596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee     */
716596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee    public CircularRevealFragment() {}
726596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee
736596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee    public CircularRevealFragment(Point touchPoint, OnCircularRevealCompleteListener listener) {
746596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee        mTouchPoint = touchPoint;
756596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee        mListener = listener;
766596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee    }
776596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee
786596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee    @Override
796596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee    public void onResume() {
806596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee        super.onResume();
816596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee        if (!mAnimationStarted) {
826596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee            // Only run the animation once for each instance of the fragment
836596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee            startOutgoingAnimation(InCallPresenter.getInstance().getThemeColors());
846596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee        }
856596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee        mAnimationStarted = true;
866596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee    }
876596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee
886596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee    @Override
896596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee    public View onCreateView(LayoutInflater inflater, ViewGroup container,
906596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee            Bundle savedInstanceState) {
916596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee        return inflater.inflate(R.layout.outgoing_call_animation, container, false);
926596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee    }
936596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee
946596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee    public void startOutgoingAnimation(MaterialPalette palette) {
956596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee        final Activity activity = getActivity();
966596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee        if (activity == null) {
976596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee            Log.w(this, "Asked to do outgoing call animation when not attached");
986596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee            return;
996596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee        }
1006596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee
1016596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee        final View view  = activity.getWindow().getDecorView();
1026596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee
1036596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee        // The circle starts from an initial size of 0 so clip it such that it is invisible.
1046596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee        // Otherwise the first frame is drawn with a fully opaque screen which causes jank. When
1056596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee        // the animation later starts, this clip will be clobbered by the circular reveal clip.
1066596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee        // See ViewAnimationUtils.createCircularReveal.
1076596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee        view.setOutlineProvider(new ViewOutlineProvider() {
1086596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee            @Override
1096596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee            public void getOutline(View view, Outline outline) {
1106596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee                // Using (0, 0, 0, 0) will not work since the outline will simply be treated as
1116596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee                // an empty outline.
1126596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee                outline.setOval(-1, -1, 0, 0);
1136596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee            }
1146596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee        });
1156596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee        view.setClipToOutline(true);
1166596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee
1176596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee        if (palette != null) {
1186596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee            view.findViewById(R.id.outgoing_call_animation_circle).setBackgroundColor(
1196596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee                    palette.mPrimaryColor);
1206596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee            activity.getWindow().setStatusBarColor(palette.mSecondaryColor);
1216596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee        }
1226596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee
1236596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee        view.getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {
1246596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee            @Override
1256596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee            public boolean onPreDraw() {
1266596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee                final ViewTreeObserver vto = view.getViewTreeObserver();
1276596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee                if (vto.isAlive()) {
1286596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee                    vto.removeOnPreDrawListener(this);
1296596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee                }
1306596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee                final Animator animator = getRevealAnimator(mTouchPoint);
131471f63aa71568456b368b80d38a5e43f7051d28dJay Shrauner                if (animator != null) {
132471f63aa71568456b368b80d38a5e43f7051d28dJay Shrauner                    animator.addListener(new AnimatorListenerAdapter() {
133471f63aa71568456b368b80d38a5e43f7051d28dJay Shrauner                        @Override
134471f63aa71568456b368b80d38a5e43f7051d28dJay Shrauner                        public void onAnimationEnd(Animator animation) {
135471f63aa71568456b368b80d38a5e43f7051d28dJay Shrauner                            view.setClipToOutline(false);
136471f63aa71568456b368b80d38a5e43f7051d28dJay Shrauner                            if (mListener != null) {
137471f63aa71568456b368b80d38a5e43f7051d28dJay Shrauner                                mListener.onCircularRevealComplete(getFragmentManager());
138471f63aa71568456b368b80d38a5e43f7051d28dJay Shrauner                            }
1396596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee                        }
140471f63aa71568456b368b80d38a5e43f7051d28dJay Shrauner                    });
141471f63aa71568456b368b80d38a5e43f7051d28dJay Shrauner                    animator.start();
142471f63aa71568456b368b80d38a5e43f7051d28dJay Shrauner                }
1436596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee                return false;
1446596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee            }
1456596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee        });
1466596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee    }
1476596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee
1486596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee    private Animator getRevealAnimator(Point touchPoint) {
1496596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee        final Activity activity = getActivity();
150471f63aa71568456b368b80d38a5e43f7051d28dJay Shrauner        if (activity == null) {
151471f63aa71568456b368b80d38a5e43f7051d28dJay Shrauner            return null;
152471f63aa71568456b368b80d38a5e43f7051d28dJay Shrauner        }
1536596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee        final View view  = activity.getWindow().getDecorView();
1546596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee        final Display display = activity.getWindowManager().getDefaultDisplay();
1556596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee        final Point size = new Point();
1566596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee        display.getSize(size);
1576596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee
1586596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee        int startX = size.x / 2;
1596596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee        int startY = size.y / 2;
1606596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee        if (touchPoint != null) {
1616596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee            startX = touchPoint.x;
1626596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee            startY = touchPoint.y;
1636596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee        }
1646596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee
1656596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee        final Animator valueAnimator = ViewAnimationUtils.createCircularReveal(view,
1666596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee                startX, startY, 0, Math.max(size.x, size.y));
1676596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee        valueAnimator.setDuration(getResources().getInteger(R.integer.reveal_animation_duration));
1686596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee        return valueAnimator;
1696596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee    }
1706596e1be9d3e15e2cbe4a5e9c3824ee47a0e2744Yorke Lee}
171