blob: 1fcdbc0eb6b56712301ce5131d441bfc87dfecbf [file] [log] [blame]
Pavel Maltsev1ecdd6c2016-03-02 16:33:44 -08001/*
2 * Copyright (C) 2016 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 */
Pavel Maltsev0477e292016-05-27 12:22:36 -070016package android.car.cluster.renderer;
Pavel Maltsev1ecdd6c2016-03-02 16:33:44 -080017
18import android.annotation.Nullable;
Pavel Maltsev8cf86912016-04-01 18:01:51 -070019import android.car.navigation.CarNavigationInstrumentCluster;
Pavel Maltsev1ecdd6c2016-03-02 16:33:44 -080020import android.graphics.Bitmap;
21import android.os.Handler;
22import android.os.Looper;
23import android.os.Message;
24
Pavel Maltsev0477e292016-05-27 12:22:36 -070025import java.lang.ref.WeakReference;
Pavel Maltsev8cf86912016-04-01 18:01:51 -070026import java.util.concurrent.CountDownLatch;
27
Pavel Maltsev1ecdd6c2016-03-02 16:33:44 -080028/**
29 * A wrapper over {@link NavigationRenderer} that runs all its methods in the context of provided
30 * looper. It is guaranteed that all calls will be invoked in order they were called.
31 */
Pavel Maltsev0477e292016-05-27 12:22:36 -070032/* package */ class ThreadSafeNavigationRenderer extends NavigationRenderer {
Pavel Maltsev1ecdd6c2016-03-02 16:33:44 -080033
34 private final Handler mHandler;
Pavel Maltsev8cf86912016-04-01 18:01:51 -070035 private final NavigationRenderer mRenderer;
Pavel Maltsev1ecdd6c2016-03-02 16:33:44 -080036
37 private final static int MSG_NAV_START = 1;
38 private final static int MSG_NAV_STOP = 2;
39 private final static int MSG_NAV_NEXT_TURN = 3;
40 private final static int MSG_NAV_NEXT_TURN_DISTANCE = 4;
41
42 /** Creates thread-safe {@link NavigationRenderer}. Returns null if renderer == null */
43 @Nullable
Pavel Maltsev0477e292016-05-27 12:22:36 -070044 static NavigationRenderer createFor(Looper looper, NavigationRenderer renderer) {
Pavel Maltsev1ecdd6c2016-03-02 16:33:44 -080045 return renderer == null ? null : new ThreadSafeNavigationRenderer(looper, renderer);
46 }
47
48 private ThreadSafeNavigationRenderer(Looper looper, NavigationRenderer renderer) {
Pavel Maltsev8cf86912016-04-01 18:01:51 -070049 mRenderer = renderer;
Pavel Maltsev1ecdd6c2016-03-02 16:33:44 -080050 mHandler = new NavigationRendererHandler(looper, renderer);
51 }
52
53 @Override
Pavel Maltsev8cf86912016-04-01 18:01:51 -070054 public CarNavigationInstrumentCluster getNavigationProperties() {
Pavel Maltsev0477e292016-05-27 12:22:36 -070055 if (mHandler.getLooper() == Looper.myLooper()) {
56 return mRenderer.getNavigationProperties();
57 } else {
58 return runAndWaitResult(mHandler,
59 new RunnableWithResult<CarNavigationInstrumentCluster>() {
60 @Override
61 protected CarNavigationInstrumentCluster createResult() {
62 return mRenderer.getNavigationProperties();
63 }
64 });
65 }
Pavel Maltsev8cf86912016-04-01 18:01:51 -070066 }
67
68 @Override
Pavel Maltsev1ecdd6c2016-03-02 16:33:44 -080069 public void onStartNavigation() {
70 mHandler.sendMessage(mHandler.obtainMessage(MSG_NAV_START));
71 }
72
73 @Override
74 public void onStopNavigation() {
75 mHandler.sendMessage(mHandler.obtainMessage(MSG_NAV_STOP));
76 }
77
78 @Override
79 public void onNextTurnChanged(int event, String road, int turnAngle, int turnNumber,
80 Bitmap image, int turnSide) {
81 mHandler.sendMessage(mHandler.obtainMessage(MSG_NAV_NEXT_TURN,
82 new NextTurn(event, road, turnAngle, turnNumber, image, turnSide)));
83 }
84
85 @Override
86 public void onNextTurnDistanceChanged(int distanceMeters, int timeSeconds) {
87 mHandler.sendMessage(mHandler.obtainMessage(
88 MSG_NAV_NEXT_TURN_DISTANCE, distanceMeters, timeSeconds));
89 }
90
91 private static class NavigationRendererHandler extends RendererHandler<NavigationRenderer> {
92
93 NavigationRendererHandler(Looper looper, NavigationRenderer renderer) {
94 super(looper, renderer);
95 }
96
97 @Override
98 public void handleMessage(Message msg, NavigationRenderer renderer) {
99
100 switch (msg.what) {
101 case MSG_NAV_START:
102 renderer.onStartNavigation();
103 break;
104 case MSG_NAV_STOP:
105 renderer.onStopNavigation();
106 break;
107 case MSG_NAV_NEXT_TURN:
108 NextTurn nt = (NextTurn) msg.obj;
109 renderer.onNextTurnChanged(nt.event, nt.road, nt.turnAngle, nt.turnNumber,
110 nt.bitmap, nt.turnSide);
111 break;
112 case MSG_NAV_NEXT_TURN_DISTANCE:
113 renderer.onNextTurnDistanceChanged(msg.arg1, msg.arg2);
114 break;
115 default:
116 throw new IllegalArgumentException("Msg: " + msg.what);
117 }
118 }
119 }
120
Pavel Maltsev8cf86912016-04-01 18:01:51 -0700121 private static <E> E runAndWaitResult(Handler handler, RunnableWithResult<E> runnable) {
Pavel Maltsev0477e292016-05-27 12:22:36 -0700122 final CountDownLatch latch = new CountDownLatch(1);
Pavel Maltsev8cf86912016-04-01 18:01:51 -0700123 handler.post(() -> {
124 runnable.run();
125 latch.countDown();
126 });
127 try {
Pavel Maltsev0477e292016-05-27 12:22:36 -0700128 latch.await();
Pavel Maltsev8cf86912016-04-01 18:01:51 -0700129 } catch (InterruptedException e) {
130 throw new RuntimeException(e);
131 }
132 return runnable.getResult();
133 }
134
Pavel Maltsev1ecdd6c2016-03-02 16:33:44 -0800135 private static class NextTurn {
136 private final int event;
137 private final String road;
138 private final int turnAngle;
139 private final int turnNumber;
140 private final Bitmap bitmap;
141 private final int turnSide;
142
143 NextTurn(int event, String road, int turnAngle, int turnNumber, Bitmap bitmap,
144 int turnSide) {
145 this.event = event;
146 this.road = road;
147 this.turnAngle = turnAngle;
148 this.turnNumber = turnNumber;
149 this.bitmap = bitmap;
150 this.turnSide = turnSide;
151 }
152 }
Pavel Maltsev8cf86912016-04-01 18:01:51 -0700153
Pavel Maltsev0477e292016-05-27 12:22:36 -0700154 private static abstract class RunnableWithResult<T> implements Runnable {
Pavel Maltsev8cf86912016-04-01 18:01:51 -0700155 private volatile T result;
156
157 protected abstract T createResult();
158
159 @Override
160 public void run() {
161 result = createResult();
162 }
163
164 public T getResult() {
165 return result;
166 }
167 }
Pavel Maltsev0477e292016-05-27 12:22:36 -0700168
169 private static abstract class RendererHandler<T> extends Handler {
170
171 private final WeakReference<T> mRendererRef;
172
173 RendererHandler(Looper looper, T renderer) {
174 super(looper);
175 mRendererRef = new WeakReference<>(renderer);
176 }
177
178 @Override
179 public void handleMessage(Message msg) {
180 T renderer = mRendererRef.get();
181 if (renderer != null) {
182 handleMessage(msg, renderer);
183 }
184 }
185
186 public abstract void handleMessage(Message msg, T renderer);
187 }
Pavel Maltsev1ecdd6c2016-03-02 16:33:44 -0800188}