1df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev/* 2df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev * Copyright (C) 2016 The Android Open Source Project 3df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev * 4df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev * Licensed under the Apache License, Version 2.0 (the "License"); 5df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev * you may not use this file except in compliance with the License. 6df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev * You may obtain a copy of the License at 7df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev * 8df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev * http://www.apache.org/licenses/LICENSE-2.0 9df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev * 10df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev * Unless required by applicable law or agreed to in writing, software 11df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev * distributed under the License is distributed on an "AS IS" BASIS, 12df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev * See the License for the specific language governing permissions and 14df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev * limitations under the License. 15df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev */ 16df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsevpackage com.android.car.cluster.sample; 17df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 18df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsevimport static com.android.car.cluster.sample.DebugUtil.DEBUG; 19df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 20df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsevimport android.annotation.Nullable; 21df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsevimport android.app.Presentation; 22df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsevimport android.car.cluster.renderer.NavigationRenderer; 23df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsevimport android.car.navigation.CarNavigationInstrumentCluster; 243388e7848f3a30029935463afafe9b8280939127Keun-young Parkimport android.car.navigation.CarNavigationStatusManager; 25df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsevimport android.content.ComponentName; 26df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsevimport android.content.ContentResolver; 27df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsevimport android.content.Context; 28df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsevimport android.content.Intent; 29df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsevimport android.content.ServiceConnection; 30df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsevimport android.content.res.Resources; 31df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsevimport android.graphics.Bitmap; 32df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsevimport android.graphics.Color; 33df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsevimport android.hardware.display.DisplayManager; 34df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsevimport android.media.MediaDescription; 35df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsevimport android.media.MediaMetadata; 36df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsevimport android.media.session.PlaybackState; 37df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsevimport android.os.Handler; 38df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsevimport android.os.IBinder; 39df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsevimport android.os.Looper; 40df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsevimport android.os.SystemClock; 41df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsevimport android.os.UserHandle; 42df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsevimport android.provider.Settings; 43df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsevimport android.telecom.Call; 44df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsevimport android.telecom.GatewayInfo; 45a928f13d7e41030331afaad33ee62e3772d96541Pavel Maltsevimport android.text.TextUtils; 46df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsevimport android.util.Log; 47a928f13d7e41030331afaad33ee62e3772d96541Pavel Maltsevimport android.util.SparseArray; 48df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsevimport android.view.Display; 49df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 50df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsevimport com.android.car.cluster.sample.MediaStateMonitor.MediaStateListener; 51df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsevimport com.android.car.cluster.sample.cards.MediaCard; 52df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsevimport com.android.car.cluster.sample.cards.NavCard; 53df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 54a928f13d7e41030331afaad33ee62e3772d96541Pavel Maltsevimport java.text.DecimalFormatSymbols; 55a928f13d7e41030331afaad33ee62e3772d96541Pavel Maltsevimport java.text.NumberFormat; 56a928f13d7e41030331afaad33ee62e3772d96541Pavel Maltsevimport java.util.Locale; 57df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsevimport java.util.Objects; 58df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsevimport java.util.Timer; 59df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsevimport java.util.TimerTask; 60df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 61df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev/** 62df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev * This class is responsible for subscribing to system events (such as call status, media status, 63df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev * etc.) and updating accordingly UI component {@link ClusterView}. 64df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev */ 65df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev/*package*/ class InstrumentClusterController { 66df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 67df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev private final static String TAG = DebugUtil.getTag(InstrumentClusterController.class); 68df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 69df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev private final Context mContext; 70df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev private final NavigationRenderer mNavigationRenderer; 71a928f13d7e41030331afaad33ee62e3772d96541Pavel Maltsev private final SparseArray<String> mDistanceUnitNames = new SparseArray<>(); 72df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 73df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev private ClusterView mClusterView; 74df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev private MediaStateMonitor mMediaStateMonitor; 75df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev private MediaStateListenerImpl mMediaStateListener; 76df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev private ClusterInCallService mInCallService; 77df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev private MessagingNotificationHandler mNotificationHandler; 78df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev private StatusBarNotificationListener mNotificationListener; 79df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev private RetriableServiceBinder mInCallServiceRetriableBinder; 80df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev private RetriableServiceBinder mNotificationServiceRetriableBinder; 81df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 82df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev InstrumentClusterController(Context context) { 83df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mContext = context; 84df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mNavigationRenderer = new NavigationRendererImpl(this); 85df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 86df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev init(); 87df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 88df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 89df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev private void init() { 90df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev grantNotificationListenerPermissionsIfNecessary(mContext); 91df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 92df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev final Display display = getInstrumentClusterDisplay(mContext); 93df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev if (DEBUG) { 94df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev Log.d(TAG, "Instrument cluster display: " + display); 95df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 96df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev if (display == null) { 97df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev return; 98df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 99df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 100a928f13d7e41030331afaad33ee62e3772d96541Pavel Maltsev initDistanceUnitNames(mContext); 101a928f13d7e41030331afaad33ee62e3772d96541Pavel Maltsev 102df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mClusterView = new ClusterView(mContext); 103df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev Presentation presentation = new InstrumentClusterPresentation(mContext, display); 104df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev presentation.setContentView(mClusterView); 105df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 106df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev // To handle incoming messages 107df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mNotificationHandler = new MessagingNotificationHandler(mClusterView); 108df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 109df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mMediaStateListener = new MediaStateListenerImpl(this); 110df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mMediaStateMonitor = new MediaStateMonitor(mContext, mMediaStateListener); 111df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 112df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mInCallServiceRetriableBinder = new RetriableServiceBinder( 113df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev new Handler(Looper.getMainLooper()), 114df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mContext, 115df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev ClusterInCallService.class, 116df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev ClusterInCallService.ACTION_LOCAL_BINDING, 117df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mInCallServiceConnection); 118df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mInCallServiceRetriableBinder.attemptToBind(); 119df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 120df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mNotificationServiceRetriableBinder = new RetriableServiceBinder( 121df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev new Handler(Looper.getMainLooper()), 122df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mContext, 123df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev StatusBarNotificationListener.class, 124df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev StatusBarNotificationListener.ACTION_LOCAL_BINDING, 125df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mNotificationListenerConnection); 126df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mNotificationServiceRetriableBinder.attemptToBind(); 127df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 128df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev // Show default card - weather 129df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mClusterView.enqueueCard(mClusterView.createWeatherCard()); 130df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 131df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev presentation.show(); 132df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 133df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 134df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev NavigationRenderer getNavigationRenderer() { 135df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev return mNavigationRenderer; 136df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 137df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 138df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev private final ServiceConnection mInCallServiceConnection = new ServiceConnection() { 139df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev @Override 140df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev public void onServiceConnected(ComponentName name, IBinder binder) { 141df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev if (DEBUG) { 142df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev Log.d(TAG, "onServiceConnected, name: " + name + ", binder: " + binder); 143df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 144df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 145df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mInCallService = ((ClusterInCallService.LocalBinder) binder).getService(); 146df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mInCallService.registerListener(mCallServiceListener); 147df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 148df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev // The InCallServiceImpl could be bound when we already have some active calls, let's 149df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev // notify UI about these calls. 150df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev for (Call call : mInCallService.getCalls()) { 151df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mCallServiceListener.onStateChanged(call, call.getState()); 152df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 153df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mInCallServiceRetriableBinder = null; 154df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 155df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 156df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev @Override 157df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev public void onServiceDisconnected(ComponentName name) { 158df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev if (DEBUG) { 159df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev Log.d(TAG, "onServiceDisconnected, name: " + name); 160df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 161df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 162df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev }; 163df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 164df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev private final ServiceConnection mNotificationListenerConnection = new ServiceConnection() { 165df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev @Override 166df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev public void onServiceConnected(ComponentName name, IBinder binder) { 167df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev if (DEBUG) { 168df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev Log.d(TAG, "onServiceConnected, name: " + name + ", binder: " + binder); 169df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 170df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 171df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mNotificationListener = ((StatusBarNotificationListener.LocalBinder) binder) 172df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev .getService(); 173df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mNotificationListener.setHandler(mNotificationHandler); 174df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 175df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mNotificationServiceRetriableBinder = null; 176df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 177df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 178df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev @Override 179df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev public void onServiceDisconnected(ComponentName name) { 180df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev if (DEBUG) { 181df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev Log.d(TAG, "onServiceDisconnected, name: "+ name); 182df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 183df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 184df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev }; 185df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 186df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev private final Call.Callback mCallServiceListener = new Call.Callback() { 187df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev @Override 188df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev public void onStateChanged(Call call, int state) { 189df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev if (DEBUG) { 190df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev Log.d(TAG, "onCallStateChanged, call: " + call + ", state: " + state); 191df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 192df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 193df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev runOnMain(() -> InstrumentClusterController.this.onCallStateChanged(call, state)); 194df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 195df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev }; 196df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 197df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev private String extractPhoneNumber(Call call) { 198df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev String number = ""; 199df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev Call.Details details = call.getDetails(); 200df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev if (details != null) { 201df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev GatewayInfo gatewayInfo = details.getGatewayInfo(); 202df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 203df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev if (gatewayInfo != null) { 204df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev number = gatewayInfo.getOriginalAddress().getSchemeSpecificPart(); 205df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } else if (details.getHandle() != null) { 206df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev number = details.getHandle().getSchemeSpecificPart(); 207df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 208df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } else { 209df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev number = mContext.getResources().getString(R.string.unknown); 210df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 211df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 212df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev return number; 213df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 214df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 215a928f13d7e41030331afaad33ee62e3772d96541Pavel Maltsev private void initDistanceUnitNames(Context context) { 2163388e7848f3a30029935463afafe9b8280939127Keun-young Park mDistanceUnitNames.put(CarNavigationStatusManager.DISTANCE_METERS, 217a928f13d7e41030331afaad33ee62e3772d96541Pavel Maltsev context.getString(R.string.nav_distance_units_meters)); 2183388e7848f3a30029935463afafe9b8280939127Keun-young Park mDistanceUnitNames.put(CarNavigationStatusManager.DISTANCE_KILOMETERS, 219a928f13d7e41030331afaad33ee62e3772d96541Pavel Maltsev context.getString(R.string.nav_distance_units_kilometers)); 2203388e7848f3a30029935463afafe9b8280939127Keun-young Park mDistanceUnitNames.put(CarNavigationStatusManager.DISTANCE_FEET, 221a928f13d7e41030331afaad33ee62e3772d96541Pavel Maltsev context.getString(R.string.nav_distance_units_ft)); 2223388e7848f3a30029935463afafe9b8280939127Keun-young Park mDistanceUnitNames.put(CarNavigationStatusManager.DISTANCE_MILES, 223a928f13d7e41030331afaad33ee62e3772d96541Pavel Maltsev context.getString(R.string.nav_distance_units_miles)); 2243388e7848f3a30029935463afafe9b8280939127Keun-young Park mDistanceUnitNames.put(CarNavigationStatusManager.DISTANCE_YARDS, 225a928f13d7e41030331afaad33ee62e3772d96541Pavel Maltsev context.getString(R.string.nav_distance_units_yards)); 226a928f13d7e41030331afaad33ee62e3772d96541Pavel Maltsev } 227a928f13d7e41030331afaad33ee62e3772d96541Pavel Maltsev 228df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev private void onCallStateChanged(Call call, int state) { 229df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev if (DEBUG) { 230df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev Log.d(TAG, "onCallStateChanged, call: " + call + ", state: " + state); 231df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 232df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 233df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev switch (state) { 234df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev case Call.STATE_ACTIVE: { 235df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev Call.Details details = call.getDetails(); 236df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev if (details != null) { 237df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev long duration = System.currentTimeMillis() - details.getConnectTimeMillis(); 238df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mClusterView.handleCallConnected(SystemClock.elapsedRealtime() - duration); 239df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 240df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } break; 241df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev case Call.STATE_CONNECTING: { 242df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 243df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } break; 244df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev case Call.STATE_DISCONNECTING: { 245df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mClusterView.handleCallDisconnected(); 246df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } break; 247df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev case Call.STATE_DIALING: { 248df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev String phoneNumber = extractPhoneNumber(call); 249df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev String displayName = TelecomUtils.getDisplayName(mContext, phoneNumber); 250df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev Bitmap image = TelecomUtils 251df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev .getContactPhotoFromNumber(mContext.getContentResolver(), phoneNumber); 252df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mClusterView.handleDialingCall(image, displayName); 253df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } break; 254df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev case Call.STATE_DISCONNECTED: { 255df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mClusterView.handleCallDisconnected(); 256df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } break; 257df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev case Call.STATE_HOLDING: 258df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev break; 259df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev case Call.STATE_NEW: 260df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev break; 261df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev case Call.STATE_RINGING: { 262df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev String phoneNumber = extractPhoneNumber(call); 263df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev String displayName = TelecomUtils.getDisplayName(mContext, phoneNumber); 264df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev Bitmap image = TelecomUtils 265df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev .getContactPhotoFromNumber(mContext.getContentResolver(), phoneNumber); 266df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev if (image != null) { 267df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev if (DEBUG) { 268df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev Log.d(TAG, "Incoming call, contact image size: " + image.getWidth() 269df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev + "x" + image.getHeight()); 270df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 271df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 272df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mClusterView.handleIncomingCall(image, displayName); 273df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } break; 274df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev default: 275df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev Log.w(TAG, "Unexpected call state: " + state + ", call : " + call); 276df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 277df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 278df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 279df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev private static void grantNotificationListenerPermissionsIfNecessary(Context context) { 280df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev ComponentName componentName = new ComponentName(context, 281df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev StatusBarNotificationListener.class); 282df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev String componentFlatten = componentName.flattenToString(); 283df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 284df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev ContentResolver resolver = context.getContentResolver(); 285df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev String grantedComponents = Settings.Secure.getString(resolver, 286df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev Settings.Secure.ENABLED_NOTIFICATION_LISTENERS); 287df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 288df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev if (grantedComponents != null) { 289df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev String[] allowed = grantedComponents.split(":"); 290df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev for (String s : allowed) { 291df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev if (s.equals(componentFlatten)) { 292df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev if (DEBUG) { 293df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev Log.d(TAG, "Notification listener permission granted."); 294df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 295df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev return; // Permission already granted. 296df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 297df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 298df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 299df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 300df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev if (DEBUG) { 301df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev Log.d(TAG, "Granting notification listener permission."); 302df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 303df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev Settings.Secure.putString(resolver, 304df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev Settings.Secure.ENABLED_NOTIFICATION_LISTENERS, 305df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev grantedComponents + ":" + componentFlatten); 306df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 307df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 308df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 309df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev /* package */ void onDestroy() { 310df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev if (mMediaStateMonitor != null) { 311df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mMediaStateMonitor.release(); 312df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mMediaStateMonitor = null; 313df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 314df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev if (mMediaStateListener != null) { 315df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mMediaStateListener.release(); 316df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mMediaStateListener = null; 317df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 318df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev if (mInCallService != null) { 319df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mContext.unbindService(mInCallServiceConnection); 320df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mInCallService = null; 321df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 322df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev if (mNotificationListener != null) { 323df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mContext.unbindService(mNotificationListenerConnection); 324df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mNotificationListener = null; 325df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 326df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev if (mInCallServiceRetriableBinder != null) { 327df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mInCallServiceRetriableBinder.release(); 328df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mInCallServiceRetriableBinder = null; 329df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 330df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev if (mNotificationServiceRetriableBinder != null) { 331df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mNotificationServiceRetriableBinder.release(); 332df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mNotificationServiceRetriableBinder = null; 333df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 334df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 335df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 336df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev private static Display getInstrumentClusterDisplay(Context context) { 337df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev DisplayManager displayManager = context.getSystemService(DisplayManager.class); 338df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev Display[] displays = displayManager.getDisplays(); 339df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 340df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev if (DEBUG) { 341df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev Log.d(TAG, "There are currently " + displays.length + " displays connected."); 342df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev for (Display display : displays) { 343df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev Log.d(TAG, " " + display); 344df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 345df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 346df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 347df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev if (displays.length > 1) { 348df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev // TODO: Put this into settings? 349df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev return displays[displays.length - 1]; 350df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 351df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev return null; 352df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 353df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 354df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev private static void runOnMain(Runnable runnable) { 355df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev new Handler(Looper.getMainLooper()).post(runnable); 356df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 357df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 358df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev private static class MediaStateListenerImpl implements MediaStateListener { 359df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev private final Timer mTimer = new Timer("ClusterMediaProgress"); 360df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev private final ClusterView mClusterView; 361df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 362df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev private MediaData mCurrentMedia; 363df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev private MediaAppInfo mMediaAppInfo; 364df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev private MediaCard mCard; 365df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev private PlaybackState mPlaybackState; 366df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev private TimerTask mTimerTask; 367df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 368df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev MediaStateListenerImpl(InstrumentClusterController renderer) { 369df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mClusterView = renderer.mClusterView; 370df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 371df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 372df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev void release() { 373df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev if (mTimerTask != null) { 374df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mTimerTask.cancel(); 375df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mTimerTask = null; 376df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 377df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 378df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 379df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev @Override 380df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev public void onPlaybackStateChanged(final PlaybackState playbackState) { 381df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev if (DEBUG) { 382df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev Log.d(TAG, "onPlaybackStateChanged, playbackState: " + playbackState); 383df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 384df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 385df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev if (mTimerTask != null) { 386df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mTimerTask.cancel(); 387df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mTimerTask = null; 388df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 389df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 390df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev if (playbackState != null) { 391df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev if ((playbackState.getState() == PlaybackState.STATE_PLAYING 392df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev || playbackState.getState() == PlaybackState.STATE_BUFFERING)) { 393df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mPlaybackState = playbackState; 394df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 395df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev if (mCurrentMedia != null) { 396df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev showMediaCardIfNecessary(mCurrentMedia); 397df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 398df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev if (mCurrentMedia.duration > 0) { 399df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev startTrackProgressTimer(); 400df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 401df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 402df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } else if (playbackState.getState() == PlaybackState.STATE_STOPPED 403df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev || playbackState.getState() == PlaybackState.STATE_ERROR 404df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev || playbackState.getState() == PlaybackState.STATE_NONE) { 405df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev hideMediaCard(); 406df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 407df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } else { 408df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev hideMediaCard(); 409df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 410df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 411df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 412df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 413df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev private void startTrackProgressTimer() { 414df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mTimerTask = new TimerTask() { 415df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev @Override 416df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev public void run() { 417df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev runOnMain(() -> { 418df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev if (mPlaybackState == null || mCard == null) { 419df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev return; 420df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 421df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev long trackStarted = mPlaybackState.getLastPositionUpdateTime() 422df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev - mPlaybackState.getPosition(); 423df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev long trackDuration = mCurrentMedia == null ? 0 : mCurrentMedia.duration; 424df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 425df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev long currentTime = SystemClock.elapsedRealtime(); 426df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev long progressMs = (currentTime - trackStarted); 427df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev if (trackDuration > 0) { 428df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mCard.setProgress((int)((progressMs * 100) / trackDuration)); 429df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 430df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev }); 431df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 432df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev }; 433df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 434df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mTimer.scheduleAtFixedRate(mTimerTask, 0, 1000); 435df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 436df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 437df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 438df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev @Override 439df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev public void onMetadataChanged(MediaMetadata metadata) { 440df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev if (DEBUG) { 441df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev Log.d(TAG, "onMetadataChanged: " + metadata); 442df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 443df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev MediaData data = MediaData.createFromMetadata(metadata); 444df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev if (data == null) { 445df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev hideMediaCard(); 446df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 447df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mCurrentMedia = data; 448df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 449df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 450df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev private void hideMediaCard() { 451df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev if (DEBUG) { 452df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev Log.d(TAG, "hideMediaCard"); 453df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 454df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 455df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev if (mCard != null) { 456df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mClusterView.removeCard(mCard); 457df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mCard = null; 458df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 459df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 460df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev // Remove all existing media cards if any. 461df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev MediaCard mediaCard; 462df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev do { 463df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mediaCard = mClusterView.getCardOrNull(MediaCard.class); 464df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev if (mediaCard != null) { 465df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mClusterView.removeCard(mediaCard); 466df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 467df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } while (mediaCard != null); 468df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 469df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 470df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev private void showMediaCardIfNecessary(MediaData data) { 471df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev if (!needToCreateMediaCard(data)) { 472df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev return; 473df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 474df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 475df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev int accentColor = mMediaAppInfo == null 476df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev ? Color.GRAY : mMediaAppInfo.getMediaClientAccentColor(); 477df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 478df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mCard = mClusterView.createMediaCard( 479df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev data.albumCover, data.title, data.subtitle, accentColor); 480df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev if (data.duration <= 0) { 481df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mCard.setProgress(100); // unknown position 482df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } else { 483df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mCard.setProgress(0); 484df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 485df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mClusterView.enqueueCard(mCard); 486df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 487df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 488df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev private boolean needToCreateMediaCard(MediaData data) { 489df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev return (mCard == null) 490df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev || !Objects.equals(mCard.getTitle(), data.title) 491df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev || !Objects.equals(mCard.getSubtitle(), data.subtitle); 492df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 493df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 494df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev @Override 495df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev public void onMediaAppChanged(MediaAppInfo mediaAppInfo) { 496df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mMediaAppInfo = mediaAppInfo; 497df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 498df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 499df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev private static class MediaData { 500df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev final Bitmap albumCover; 501df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev final String subtitle; 502df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev final String title; 503df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev final long duration; 504df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 505df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev private MediaData(MediaMetadata metadata) { 506df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev MediaDescription mediaDescription = metadata.getDescription(); 507df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev title = charSequenceToString(mediaDescription.getTitle()); 508df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev subtitle = charSequenceToString(mediaDescription.getSubtitle()); 509df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev albumCover = mediaDescription.getIconBitmap(); 510df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev duration = metadata.getLong(MediaMetadata.METADATA_KEY_DURATION); 511df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 512df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 513df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev static MediaData createFromMetadata(MediaMetadata metadata) { 514df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev return metadata == null ? null : new MediaData(metadata); 515df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 516df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 517df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev private static String charSequenceToString(@Nullable CharSequence cs) { 518df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev return cs == null ? null : String.valueOf(cs); 519df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 520df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 521df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev @Override 522df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev public String toString() { 523df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev return "MediaData{" + 524df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev "albumCover=" + albumCover + 525df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev ", subtitle='" + subtitle + '\'' + 526df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev ", title='" + title + '\'' + 527df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev ", duration=" + duration + 528df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev '}'; 529df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 530df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 531df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 532df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 533df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev private static class NavigationRendererImpl extends NavigationRenderer { 534df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 535df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev private final InstrumentClusterController mController; 536df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 537df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev private ClusterView mClusterView; 538df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev private Resources mResources; 539df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 540df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev private NavCard mNavCard; 541df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 542df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev NavigationRendererImpl(InstrumentClusterController controller) { 543df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mController = controller; 544df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 545df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 546df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev @Override 547df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev public CarNavigationInstrumentCluster getNavigationProperties() { 548df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev if (DEBUG) { 549df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev Log.d(TAG, "getNavigationProperties"); 550df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 551df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev return CarNavigationInstrumentCluster.createCustomImageCluster( 552df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 1000, /* 1 Hz*/ 553df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 64, /* image width */ 554df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 64, /* image height */ 555df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 32); /* color depth */ 556df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 557df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 558df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev @Override 559df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev public void onStartNavigation() { 560df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev if (DEBUG) { 561df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev Log.d(TAG, "onStartNavigation"); 562df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 563df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mClusterView = mController.mClusterView; 564df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mResources = mController.mContext.getResources(); 565df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mNavCard = mClusterView.createNavCard(); 566df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 567df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 568df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev @Override 569df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev public void onStopNavigation() { 570df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev if (DEBUG) { 571df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev Log.d(TAG, "onStopNavigation"); 572df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 573df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 574df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev if (mNavCard != null) { 575df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mNavCard.removeGracefully(); 576df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mNavCard = null; 577df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 578df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 579df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 580df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev @Override 5813388e7848f3a30029935463afafe9b8280939127Keun-young Park public void onNextTurnChanged(int event, CharSequence eventName, int turnAngle, 5823388e7848f3a30029935463afafe9b8280939127Keun-young Park int turnNumber, Bitmap image, int turnSide) { 583df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev if (DEBUG) { 5843388e7848f3a30029935463afafe9b8280939127Keun-young Park Log.d(TAG, "onNextTurnChanged, eventName: " + eventName + ", image: " + image + 585df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev (image == null ? "" : ", size: " 586df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev + image.getWidth() + "x" + image.getHeight())); 587df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 588df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mNavCard.setManeuverImage(BitmapUtils.generateNavManeuverIcon( 589df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev (int) mResources.getDimension(R.dimen.card_icon_size), 590df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mResources.getColor(R.color.maps_background, null), 591df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev image)); 5923388e7848f3a30029935463afafe9b8280939127Keun-young Park mNavCard.setStreet(eventName); 593df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev if (!mClusterView.cardExists(mNavCard)) { 594df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mClusterView.enqueueCard(mNavCard); 595df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 596df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 597df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 598df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev @Override 599a928f13d7e41030331afaad33ee62e3772d96541Pavel Maltsev public void onNextTurnDistanceChanged(int meters, int timeSeconds, 6003388e7848f3a30029935463afafe9b8280939127Keun-young Park int displayDistanceMillis, int distanceUnit) { 601df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev if (DEBUG) { 602df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev Log.d(TAG, "onNextTurnDistanceChanged, distanceMeters: " + meters 603a928f13d7e41030331afaad33ee62e3772d96541Pavel Maltsev + ", timeSeconds: " + timeSeconds 604a928f13d7e41030331afaad33ee62e3772d96541Pavel Maltsev + ", displayDistanceMillis: " + displayDistanceMillis 6053388e7848f3a30029935463afafe9b8280939127Keun-young Park + ", DistanceUnit: " + distanceUnit); 606df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 607df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 608a928f13d7e41030331afaad33ee62e3772d96541Pavel Maltsev int remainder = displayDistanceMillis % 1000; 6093388e7848f3a30029935463afafe9b8280939127Keun-young Park String decimalPart = (remainder != 0) 610a928f13d7e41030331afaad33ee62e3772d96541Pavel Maltsev ? String.format("%c%d", 611a928f13d7e41030331afaad33ee62e3772d96541Pavel Maltsev DecimalFormatSymbols.getInstance().getDecimalSeparator(), 612a928f13d7e41030331afaad33ee62e3772d96541Pavel Maltsev remainder) 613a928f13d7e41030331afaad33ee62e3772d96541Pavel Maltsev : ""; 614a928f13d7e41030331afaad33ee62e3772d96541Pavel Maltsev 615a928f13d7e41030331afaad33ee62e3772d96541Pavel Maltsev String distanceToDisplay = (displayDistanceMillis / 1000) + decimalPart; 6163388e7848f3a30029935463afafe9b8280939127Keun-young Park String unitsToDisplay = mController.mDistanceUnitNames.get(distanceUnit); 617a928f13d7e41030331afaad33ee62e3772d96541Pavel Maltsev 618a928f13d7e41030331afaad33ee62e3772d96541Pavel Maltsev mNavCard.setDistanceToNextManeuver(distanceToDisplay, unitsToDisplay); 619df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 620df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 621df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 622df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev /** 623df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev * Services might not be ready for binding. This class will retry binding after short interval 624df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev * if previous binding failed. 625df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev */ 626df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev private static class RetriableServiceBinder { 627df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev private static final long RETRY_INTERVAL_MS = 500; 628df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev private static final long MAX_RETRY = 30; 629df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 630df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev private Handler mHandler; 631df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev private final Context mContext; 632df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev private final Intent mIntent; 633df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev private final ServiceConnection mConnection; 634df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 635df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev private long mAttemptsLeft = MAX_RETRY; 636df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 637df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev private final Runnable mBindRunnable = () -> attemptToBind(); 638df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 639df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev RetriableServiceBinder(Handler handler, Context context, Class<?> cls, String action, 640df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev ServiceConnection connection) { 641df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mHandler = handler; 642df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mContext = context; 643df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mIntent = new Intent(mContext, cls); 644df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mIntent.setAction(action); 645df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mConnection = connection; 646df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 647df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 648df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev void release() { 649df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mHandler.removeCallbacks(mBindRunnable); 650df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 651df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 652df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev void attemptToBind() { 653df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev boolean bound = mContext.bindServiceAsUser(mIntent, 654df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mConnection, Context.BIND_AUTO_CREATE, UserHandle.CURRENT_OR_SELF); 655df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev 656df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev if (!bound && --mAttemptsLeft > 0) { 657df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev mHandler.postDelayed(mBindRunnable, RETRY_INTERVAL_MS); 658df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } else if (!bound) { 659df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev Log.e(TAG, "Gave up to bind to a service: " + mIntent.getComponent() + " after " 660df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev + MAX_RETRY + " retries."); 661df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 662df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 663df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev } 664df4db1d3efb85be01f0973067c9e104b5f93f205Pavel Maltsev} 665