Home | History | Annotate | Download | only in impl
      1 /*
      2  * Copyright (C) 2017 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.dialer.simulator.impl;
     18 
     19 import android.graphics.Canvas;
     20 import android.graphics.Color;
     21 import android.graphics.Paint;
     22 import android.os.Handler;
     23 import android.os.HandlerThread;
     24 import android.support.annotation.NonNull;
     25 import android.support.annotation.VisibleForTesting;
     26 import android.support.annotation.WorkerThread;
     27 import android.view.Surface;
     28 import com.android.dialer.common.Assert;
     29 import com.android.dialer.common.LogUtil;
     30 
     31 /**
     32  * Used by the video provider to draw the remote party's video. The in-call UI is responsible for
     33  * setting the view to draw to. Since the simulator doesn't have a remote party we simply draw a
     34  * green screen with a ball bouncing around.
     35  */
     36 final class SimulatorRemoteVideo {
     37   @NonNull private final RenderThread thread;
     38   private boolean isStopped;
     39 
     40   SimulatorRemoteVideo(@NonNull Surface surface) {
     41     thread = new RenderThread(new Renderer(surface));
     42   }
     43 
     44   void startVideo() {
     45     LogUtil.enterBlock("SimulatorRemoteVideo.startVideo");
     46     Assert.checkState(!isStopped);
     47     thread.start();
     48   }
     49 
     50   void stopVideo() {
     51     LogUtil.enterBlock("SimulatorRemoteVideo.stopVideo");
     52     isStopped = true;
     53     thread.quitSafely();
     54   }
     55 
     56   @VisibleForTesting
     57   Runnable getRenderer() {
     58     return thread.getRenderer();
     59   }
     60 
     61   private static class Renderer implements Runnable {
     62     private static final int FRAME_DELAY_MILLIS = 33;
     63     private static final float CIRCLE_STEP = 16.0f;
     64 
     65     @NonNull private final Surface surface;
     66     private float circleX;
     67     private float circleY;
     68     private float radius;
     69     private double angle;
     70 
     71     Renderer(@NonNull Surface surface) {
     72       this.surface = Assert.isNotNull(surface);
     73     }
     74 
     75     @Override
     76     public void run() {
     77       drawFrame();
     78       schedule();
     79     }
     80 
     81     @WorkerThread
     82     void schedule() {
     83       Assert.isWorkerThread();
     84       new Handler().postDelayed(this, FRAME_DELAY_MILLIS);
     85     }
     86 
     87     @WorkerThread
     88     private void drawFrame() {
     89       Assert.isWorkerThread();
     90       Canvas canvas;
     91       try {
     92         canvas = surface.lockCanvas(null /* dirtyRect */);
     93       } catch (IllegalArgumentException e) {
     94         // This can happen when the video fragment tears down.
     95         LogUtil.e("SimulatorRemoteVideo.RenderThread.drawFrame", "unable to lock canvas", e);
     96         return;
     97       }
     98 
     99       LogUtil.i(
    100           "SimulatorRemoteVideo.RenderThread.drawFrame",
    101           "size; %d x %d",
    102           canvas.getWidth(),
    103           canvas.getHeight());
    104       canvas.drawColor(Color.GREEN);
    105       moveCircle(canvas);
    106       drawCircle(canvas);
    107       surface.unlockCanvasAndPost(canvas);
    108     }
    109 
    110     @WorkerThread
    111     private void moveCircle(Canvas canvas) {
    112       Assert.isWorkerThread();
    113       int width = canvas.getWidth();
    114       int height = canvas.getHeight();
    115       if (circleX == 0 && circleY == 0) {
    116         circleX = width / 2.0f;
    117         circleY = height / 2.0f;
    118         angle = Math.PI / 4.0;
    119         radius = Math.min(canvas.getWidth(), canvas.getHeight()) * 0.15f;
    120       } else {
    121         circleX += (float) Math.cos(angle) * CIRCLE_STEP;
    122         circleY += (float) Math.sin(angle) * CIRCLE_STEP;
    123         // Bounce the circle off the edge.
    124         if (circleX + radius >= width
    125             || circleX - radius <= 0
    126             || circleY + radius >= height
    127             || circleY - radius <= 0) {
    128           angle += Math.PI / 2.0;
    129         }
    130       }
    131     }
    132 
    133     @WorkerThread
    134     private void drawCircle(Canvas canvas) {
    135       Assert.isWorkerThread();
    136       Paint paint = new Paint();
    137       paint.setColor(Color.MAGENTA);
    138       paint.setStyle(Paint.Style.FILL);
    139       canvas.drawCircle(circleX, circleY, radius, paint);
    140     }
    141   }
    142 
    143   private static class RenderThread extends HandlerThread {
    144     @NonNull private final Renderer renderer;
    145 
    146     RenderThread(@NonNull Renderer renderer) {
    147       super("SimulatorRemoteVideo");
    148       this.renderer = Assert.isNotNull(renderer);
    149     }
    150 
    151     @Override
    152     @WorkerThread
    153     protected void onLooperPrepared() {
    154       Assert.isWorkerThread();
    155       renderer.schedule();
    156     }
    157 
    158     @VisibleForTesting
    159     Runnable getRenderer() {
    160       return renderer;
    161     }
    162   }
    163 }
    164