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 SimulatorRemoteVideo(@onNull Surface surface)40 SimulatorRemoteVideo(@NonNull Surface surface) { 41 thread = new RenderThread(new Renderer(surface)); 42 } 43 startVideo()44 void startVideo() { 45 LogUtil.enterBlock("SimulatorRemoteVideo.startVideo"); 46 Assert.checkState(!isStopped); 47 thread.start(); 48 } 49 stopVideo()50 void stopVideo() { 51 LogUtil.enterBlock("SimulatorRemoteVideo.stopVideo"); 52 isStopped = true; 53 thread.quitSafely(); 54 } 55 56 @VisibleForTesting getRenderer()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 Renderer(@onNull Surface surface)71 Renderer(@NonNull Surface surface) { 72 this.surface = Assert.isNotNull(surface); 73 } 74 75 @Override run()76 public void run() { 77 drawFrame(); 78 schedule(); 79 } 80 81 @WorkerThread schedule()82 void schedule() { 83 Assert.isWorkerThread(); 84 new Handler().postDelayed(this, FRAME_DELAY_MILLIS); 85 } 86 87 @WorkerThread drawFrame()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 moveCircle(Canvas canvas)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 drawCircle(Canvas canvas)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 RenderThread(@onNull Renderer renderer)146 RenderThread(@NonNull Renderer renderer) { 147 super("SimulatorRemoteVideo"); 148 this.renderer = Assert.isNotNull(renderer); 149 } 150 151 @Override 152 @WorkerThread onLooperPrepared()153 protected void onLooperPrepared() { 154 Assert.isWorkerThread(); 155 renderer.schedule(); 156 } 157 158 @VisibleForTesting getRenderer()159 Runnable getRenderer() { 160 return renderer; 161 } 162 } 163 } 164