• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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  */
16 package android.car.cluster.renderer;
17 
18 import android.annotation.Nullable;
19 import android.car.navigation.CarNavigationInstrumentCluster;
20 import android.graphics.Bitmap;
21 import android.os.Handler;
22 import android.os.Looper;
23 import android.os.Message;
24 
25 import java.lang.ref.WeakReference;
26 import java.util.concurrent.CountDownLatch;
27 
28 /**
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  */
32 /* package */ class ThreadSafeNavigationRenderer extends NavigationRenderer {
33 
34     private final Handler mHandler;
35     private final NavigationRenderer mRenderer;
36 
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
createFor(Looper looper, NavigationRenderer renderer)44     static NavigationRenderer createFor(Looper looper, NavigationRenderer renderer) {
45         return renderer == null ? null : new ThreadSafeNavigationRenderer(looper, renderer);
46     }
47 
ThreadSafeNavigationRenderer(Looper looper, NavigationRenderer renderer)48     private ThreadSafeNavigationRenderer(Looper looper, NavigationRenderer renderer) {
49         mRenderer = renderer;
50         mHandler = new NavigationRendererHandler(looper, renderer);
51     }
52 
53     @Override
getNavigationProperties()54     public CarNavigationInstrumentCluster getNavigationProperties() {
55         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         }
66     }
67 
68     @Override
69     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, CharSequence eventName, int turnAngle, int turnNumber,
80             Bitmap image, int turnSide) {
81         mHandler.sendMessage(mHandler.obtainMessage(MSG_NAV_NEXT_TURN,
82                 new NextTurn(event, eventName, turnAngle, turnNumber, image, turnSide)));
83     }
84 
85     @Override
86     public void onNextTurnDistanceChanged(int distanceMeters, int timeSeconds,
87             int displayDistanceMillis, int displayDistanceUnit) {
88         ManeuverDistance distance = new ManeuverDistance(distanceMeters, timeSeconds,
89                 displayDistanceMillis, displayDistanceUnit);
90         mHandler.sendMessage(mHandler.obtainMessage(MSG_NAV_NEXT_TURN_DISTANCE, distance));
91     }
92 
93     private static class NavigationRendererHandler extends RendererHandler<NavigationRenderer> {
94 
95         NavigationRendererHandler(Looper looper, NavigationRenderer renderer) {
96             super(looper, renderer);
97         }
98 
99         @Override
100         public void handleMessage(Message msg, NavigationRenderer renderer) {
101             switch (msg.what) {
102                 case MSG_NAV_START:
103                     renderer.onStartNavigation();
104                     break;
105                 case MSG_NAV_STOP:
106                     renderer.onStopNavigation();
107                     break;
108                 case MSG_NAV_NEXT_TURN:
109                     NextTurn nt = (NextTurn) msg.obj;
110                     renderer.onNextTurnChanged(nt.event, nt.eventName, nt.turnAngle, nt.turnNumber,
111                             nt.bitmap, nt.turnSide);
112                     break;
113                 case MSG_NAV_NEXT_TURN_DISTANCE:
114                     ManeuverDistance d = (ManeuverDistance) msg.obj;
115                     renderer.onNextTurnDistanceChanged(
116                             d.meters, d.seconds, d.displayDistanceMillis, d.displayDistanceUnit);
117                     break;
118                 default:
119                     throw new IllegalArgumentException("Msg: " + msg.what);
120             }
121         }
122     }
123 
124     private static <E> E runAndWaitResult(Handler handler, final RunnableWithResult<E> runnable) {
125         final CountDownLatch latch = new CountDownLatch(1);
126 
127         Runnable wrappedRunnable = new Runnable() {
128             @Override
129             public void run() {
130                 runnable.run();
131                 latch.countDown();
132             }
133         };
134 
135         handler.post(wrappedRunnable);
136 
137         try {
138             latch.await();
139         } catch (InterruptedException e) {
140             throw new RuntimeException(e);
141         }
142         return runnable.getResult();
143     }
144 
145     private static class NextTurn {
146         private final int event;
147         private final CharSequence eventName;
148         private final int turnAngle;
149         private final int turnNumber;
150         private final Bitmap bitmap;
151         private final int turnSide;
152 
153         NextTurn(int event, CharSequence eventName, int turnAngle, int turnNumber, Bitmap bitmap,
154                 int turnSide) {
155             this.event = event;
156             this.eventName = eventName;
157             this.turnAngle = turnAngle;
158             this.turnNumber = turnNumber;
159             this.bitmap = bitmap;
160             this.turnSide = turnSide;
161         }
162     }
163 
164     private static abstract class RunnableWithResult<T> implements Runnable {
165         private volatile T result;
166 
167         protected abstract T createResult();
168 
169         @Override
170         public void run() {
171             result = createResult();
172         }
173 
174         public T getResult() {
175             return result;
176         }
177     }
178 
179     private static abstract class RendererHandler<T> extends Handler {
180 
181         private final WeakReference<T> mRendererRef;
182 
183         RendererHandler(Looper looper, T renderer) {
184             super(looper);
185             mRendererRef = new WeakReference<>(renderer);
186         }
187 
188         @Override
189         public void handleMessage(Message msg) {
190             T renderer = mRendererRef.get();
191             if (renderer != null) {
192                 handleMessage(msg, renderer);
193             }
194         }
195 
196         public abstract void handleMessage(Message msg, T renderer);
197     }
198 
199     private static class ManeuverDistance {
200         final int meters;
201         final int seconds;
202         final int displayDistanceMillis;
203         final int displayDistanceUnit;
204 
205         ManeuverDistance(int meters, int seconds, int displayDistanceMillis,
206                 int displayDistanceUnit) {
207             this.meters = meters;
208             this.seconds = seconds;
209             this.displayDistanceMillis = displayDistanceMillis;
210             this.displayDistanceUnit = displayDistanceUnit;
211         }
212     }
213 }
214