Home | History | Annotate | Download | only in incallui
      1 /*
      2  * Copyright (C) 2014 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 
     17 package com.android.incallui;
     18 
     19 import android.animation.Animator;
     20 import android.animation.AnimatorListenerAdapter;
     21 import android.app.Activity;
     22 import android.content.BroadcastReceiver;
     23 import android.content.Context;
     24 import android.content.Intent;
     25 import android.content.IntentFilter;
     26 import android.graphics.Outline;
     27 import android.graphics.Point;
     28 import android.os.Bundle;
     29 import android.support.v4.content.LocalBroadcastManager;
     30 import android.view.Display;
     31 import android.view.View;
     32 import android.view.ViewAnimationUtils;
     33 import android.view.ViewOutlineProvider;
     34 import android.view.ViewTreeObserver;
     35 import android.view.ViewTreeObserver.OnPreDrawListener;
     36 
     37 import com.android.contacts.common.interactions.TouchPointManager;
     38 import com.android.contacts.common.util.MaterialColorMapUtils.MaterialPalette;
     39 
     40 /**
     41  * Lightweight activity used to display a circular reveal while InCallActivity is starting up.
     42  * A BroadcastReceiver is used to listen to broadcasts from a LocalBroadcastManager to finish
     43  * the activity at suitable times.
     44  */
     45 public class CircularRevealActivity extends Activity {
     46     private static final int REVEAL_DURATION = 333;
     47     public static final String EXTRA_THEME_COLORS = "extra_theme_colors";
     48     public static final String ACTION_CLEAR_DISPLAY = "action_clear_display";
     49 
     50     final BroadcastReceiver mClearDisplayReceiver = new BroadcastReceiver( ) {
     51         @Override
     52         public void onReceive(Context context, Intent intent) {
     53             clearDisplay();
     54         }
     55     };
     56 
     57     @Override
     58     protected void onCreate(Bundle savedInstanceState) {
     59         super.onCreate(savedInstanceState);
     60         overridePendingTransition(0, 0);
     61         setContentView(R.layout.outgoing_call_animation);
     62         final Point touchPoint = getIntent().getParcelableExtra(TouchPointManager.TOUCH_POINT);
     63         final MaterialPalette palette = getIntent().getParcelableExtra(EXTRA_THEME_COLORS);
     64         setupDecorView(touchPoint, palette);
     65     }
     66 
     67     @Override
     68     protected void onStart() {
     69         super.onStart();
     70         if (!InCallPresenter.getInstance().isServiceBound()) {
     71             clearDisplay();
     72         }
     73         final IntentFilter filter = new IntentFilter();
     74         filter.addAction(ACTION_CLEAR_DISPLAY);
     75         LocalBroadcastManager.getInstance(this).registerReceiver(mClearDisplayReceiver, filter);
     76     }
     77 
     78     @Override
     79     protected void onStop() {
     80         LocalBroadcastManager.getInstance(this).unregisterReceiver(mClearDisplayReceiver);
     81         super.onStop();
     82     }
     83 
     84     private void setupDecorView(final Point touchPoint, MaterialPalette palette) {
     85         final View view  = getWindow().getDecorView();
     86 
     87         // The circle starts from an initial size of 0 so clip it such that it is invisible. When
     88         // the animation later starts, this clip will be clobbered by the circular reveal clip.
     89         // See ViewAnimationUtils.createCircularReveal.
     90         view.setOutlineProvider(new ViewOutlineProvider() {
     91             @Override
     92             public void getOutline(View view, Outline outline) {
     93                 // Using (0, 0, 0, 0) will not work since the outline will simply be treated as
     94                 // an empty outline.
     95                 outline.setOval(-1, -1, 0, 0);
     96             }
     97         });
     98         view.setClipToOutline(true);
     99 
    100         if (palette != null) {
    101             view.findViewById(R.id.outgoing_call_animation_circle).setBackgroundColor(
    102                     palette.mPrimaryColor);
    103             getWindow().setStatusBarColor(palette.mSecondaryColor);
    104         }
    105 
    106         view.getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {
    107             @Override
    108             public boolean onPreDraw() {
    109                 final ViewTreeObserver vto = view.getViewTreeObserver();
    110                 if (vto.isAlive()) {
    111                     vto.removeOnPreDrawListener(this);
    112                 }
    113                 final Animator animator = getRevealAnimator(touchPoint);
    114                 // Since this animator is a RenderNodeAnimator (native animator), add an arbitary
    115                 // start delay to force the onAnimationStart callback to happen later on the UI
    116                 // thread. Otherwise it would happen right away inside animator.start()
    117                 animator.setStartDelay(5);
    118                 animator.addListener(new AnimatorListenerAdapter() {
    119                     @Override
    120                     public void onAnimationStart(Animator animation) {
    121                         InCallPresenter.getInstance().onCircularRevealStarted(
    122                                 CircularRevealActivity.this);
    123                     }
    124 
    125                     @Override
    126                     public void onAnimationEnd(Animator animation) {
    127                         view.setClipToOutline(false);
    128                         super.onAnimationEnd(animation);
    129                     }
    130                 });
    131                 animator.start();
    132                 return false;
    133             }
    134         });
    135     }
    136 
    137     private void clearDisplay() {
    138         getWindow().getDecorView().setVisibility(View.INVISIBLE);
    139         finish();
    140     }
    141 
    142     @Override
    143     public void onBackPressed() {
    144         return;
    145     }
    146 
    147     public static void sendClearDisplayBroadcast(Context context) {
    148         LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent(ACTION_CLEAR_DISPLAY));
    149     }
    150 
    151     private Animator getRevealAnimator(Point touchPoint) {
    152         final View view  = getWindow().getDecorView();
    153         final Display display = getWindowManager().getDefaultDisplay();
    154         final Point size = new Point();
    155         display.getSize(size);
    156 
    157         int startX = size.x / 2;
    158         int startY = size.y / 2;
    159         if (touchPoint != null) {
    160             startX = touchPoint.x;
    161             startY = touchPoint.y;
    162         }
    163 
    164         final Animator valueAnimator = ViewAnimationUtils.createCircularReveal(view,
    165                 startX, startY, 0, Math.max(size.x, size.y));
    166         valueAnimator.setDuration(REVEAL_DURATION);
    167         return valueAnimator;
    168     }
    169 }
    170