Merge "Showing turn-by-turn directions only when navigation focus is active"
diff --git a/tests/DirectRenderingClusterSample/AndroidManifest.xml b/tests/DirectRenderingClusterSample/AndroidManifest.xml
index be7026c..c9ec9b6 100644
--- a/tests/DirectRenderingClusterSample/AndroidManifest.xml
+++ b/tests/DirectRenderingClusterSample/AndroidManifest.xml
@@ -43,7 +43,7 @@
     <application android:label="@string/app_name"
                  android:icon="@mipmap/ic_launcher"
                  android:directBootAware="true">
-        <service android:name=".SampleClusterServiceImpl"
+        <service android:name=".ClusterRenderingServiceImpl"
                  android:exported="false"
                  android:singleUser="true"
                  android:permission="android.car.permission.BIND_INSTRUMENT_CLUSTER_RENDERER_SERVICE"/>
diff --git a/tests/DirectRenderingClusterSample/res/layout/activity_main.xml b/tests/DirectRenderingClusterSample/res/layout/activity_main.xml
index 393db54..2cb9662 100644
--- a/tests/DirectRenderingClusterSample/res/layout/activity_main.xml
+++ b/tests/DirectRenderingClusterSample/res/layout/activity_main.xml
@@ -60,29 +60,12 @@
                 android:layout_width="20dp"
                 android:layout_height="1dp" />
 
-            <ImageView
-                android:id="@+id/maneuver"
-                android:layout_width="48dp"
-                android:layout_height="48dp"
-                android:layout_margin="10dp"
-                android:tint="@android:color/white"/>
+            <include
+                android:id="@+id/navigation_state"
+                layout="@layout/include_navigation_state"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"/>
 
-            <LinearLayout
-                android:layout_width="250dp"
-                android:layout_height="wrap_content"
-                android:orientation="vertical">
-
-                <TextView
-                    android:id="@+id/distance"
-                    android:layout_height="wrap_content"
-                    android:layout_width="wrap_content"
-                    android:textSize="30sp"/>
-                <TextView
-                    android:id="@+id/segment"
-                    android:layout_height="wrap_content"
-                    android:layout_width="wrap_content"
-                    android:textSize="18sp"/>
-            </LinearLayout>
         </LinearLayout>
     </LinearLayout>
 
diff --git a/tests/DirectRenderingClusterSample/res/layout/include_navigation_state.xml b/tests/DirectRenderingClusterSample/res/layout/include_navigation_state.xml
new file mode 100644
index 0000000..1946f0c
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/layout/include_navigation_state.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout  xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:orientation="horizontal">
+
+    <ImageView
+        android:id="@+id/maneuver"
+        android:layout_width="48dp"
+        android:layout_height="48dp"
+        android:layout_margin="10dp"
+        android:tint="@android:color/white"/>
+
+    <LinearLayout
+        android:layout_width="250dp"
+        android:layout_height="wrap_content"
+        android:orientation="vertical">
+
+        <TextView
+            android:id="@+id/distance"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:textSize="30sp"/>
+        <TextView
+            android:id="@+id/segment"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:textSize="18sp"/>
+    </LinearLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/DirectRenderingClusterSample/src/android/car/cluster/sample/SampleClusterServiceImpl.java b/tests/DirectRenderingClusterSample/src/android/car/cluster/sample/ClusterRenderingServiceImpl.java
similarity index 97%
rename from tests/DirectRenderingClusterSample/src/android/car/cluster/sample/SampleClusterServiceImpl.java
rename to tests/DirectRenderingClusterSample/src/android/car/cluster/sample/ClusterRenderingServiceImpl.java
index f8a5b06..c68f722 100644
--- a/tests/DirectRenderingClusterSample/src/android/car/cluster/sample/SampleClusterServiceImpl.java
+++ b/tests/DirectRenderingClusterSample/src/android/car/cluster/sample/ClusterRenderingServiceImpl.java
@@ -69,7 +69,7 @@
  * Implementation of {@link InstrumentClusterRenderingService} which renders an activity on a
  * virtual display that is transmitted to an external screen.
  */
-public class SampleClusterServiceImpl extends InstrumentClusterRenderingService {
+public class ClusterRenderingServiceImpl extends InstrumentClusterRenderingService {
     private static final String TAG = "Cluster.SampleService";
 
     private static final int NO_DISPLAY = -1;
@@ -118,9 +118,9 @@
     };
 
     private static class UserReceiver extends BroadcastReceiver {
-        private WeakReference<SampleClusterServiceImpl> mService;
+        private WeakReference<ClusterRenderingServiceImpl> mService;
 
-        UserReceiver(SampleClusterServiceImpl service) {
+        UserReceiver(ClusterRenderingServiceImpl service) {
             mService = new WeakReference<>(service);
         }
 
@@ -136,16 +136,16 @@
 
         @Override
         public void onReceive(Context context, Intent intent) {
-            SampleClusterServiceImpl service = mService.get();
+            ClusterRenderingServiceImpl service = mService.get();
             Log.d(TAG, "Broadcast received: " + intent);
             service.tryLaunchActivity();
         }
     }
 
     private static class MessageHandler extends Handler {
-        private final WeakReference<SampleClusterServiceImpl> mService;
+        private final WeakReference<ClusterRenderingServiceImpl> mService;
 
-        MessageHandler(SampleClusterServiceImpl service) {
+        MessageHandler(ClusterRenderingServiceImpl service) {
             mService = new WeakReference<>(service);
         }
 
@@ -364,7 +364,7 @@
             }
             case "destroyOverlayDisplay": {
                 Settings.Global.putString(getContentResolver(),
-                    Global.OVERLAY_DISPLAY_DEVICES, "");
+                        Global.OVERLAY_DISPLAY_DEVICES, "");
                 break;
             }
 
diff --git a/tests/DirectRenderingClusterSample/src/android/car/cluster/sample/MainClusterActivity.java b/tests/DirectRenderingClusterSample/src/android/car/cluster/sample/MainClusterActivity.java
index 2e38054..7464552 100644
--- a/tests/DirectRenderingClusterSample/src/android/car/cluster/sample/MainClusterActivity.java
+++ b/tests/DirectRenderingClusterSample/src/android/car/cluster/sample/MainClusterActivity.java
@@ -16,17 +16,20 @@
 package android.car.cluster.sample;
 
 import static android.car.cluster.CarInstrumentClusterManager.CATEGORY_NAVIGATION;
-import static android.car.cluster.sample.SampleClusterServiceImpl.LOCAL_BINDING_ACTION;
-import static android.car.cluster.sample.SampleClusterServiceImpl.MSG_KEY_ACTIVITY_DISPLAY_ID;
-import static android.car.cluster.sample.SampleClusterServiceImpl.MSG_KEY_ACTIVITY_STATE;
-import static android.car.cluster.sample.SampleClusterServiceImpl.MSG_KEY_CATEGORY;
-import static android.car.cluster.sample.SampleClusterServiceImpl.MSG_KEY_KEY_EVENT;
-import static android.car.cluster.sample.SampleClusterServiceImpl.MSG_ON_KEY_EVENT;
-import static android.car.cluster.sample.SampleClusterServiceImpl.MSG_ON_NAVIGATION_STATE_CHANGED;
-import static android.car.cluster.sample.SampleClusterServiceImpl.MSG_REGISTER_CLIENT;
-import static android.car.cluster.sample.SampleClusterServiceImpl.MSG_SET_ACTIVITY_LAUNCH_OPTIONS;
-import static android.car.cluster.sample.SampleClusterServiceImpl.MSG_UNREGISTER_CLIENT;
+import static android.car.cluster.sample.ClusterRenderingServiceImpl.LOCAL_BINDING_ACTION;
+import static android.car.cluster.sample.ClusterRenderingServiceImpl.MSG_KEY_ACTIVITY_DISPLAY_ID;
+import static android.car.cluster.sample.ClusterRenderingServiceImpl.MSG_KEY_ACTIVITY_STATE;
+import static android.car.cluster.sample.ClusterRenderingServiceImpl.MSG_KEY_CATEGORY;
+import static android.car.cluster.sample.ClusterRenderingServiceImpl.MSG_KEY_KEY_EVENT;
+import static android.car.cluster.sample.ClusterRenderingServiceImpl.MSG_ON_KEY_EVENT;
+import static android.car.cluster.sample.ClusterRenderingServiceImpl.MSG_ON_NAVIGATION_STATE_CHANGED;
+import static android.car.cluster.sample.ClusterRenderingServiceImpl.MSG_REGISTER_CLIENT;
+import static android.car.cluster.sample.ClusterRenderingServiceImpl.MSG_SET_ACTIVITY_LAUNCH_OPTIONS;
+import static android.car.cluster.sample.ClusterRenderingServiceImpl.MSG_UNREGISTER_CLIENT;
 
+import android.car.Car;
+import android.car.CarAppFocusManager;
+import android.car.CarNotConnectedException;
 import android.car.cluster.ClusterActivityState;
 import android.content.ComponentName;
 import android.content.Intent;
@@ -74,7 +77,8 @@
     private Messenger mService;
     private Messenger mServiceCallbacks = new Messenger(new MessageHandler(this));
     private VirtualDisplay mPendingVirtualDisplay = null;
-    private final Handler mHandler = new Handler();
+    private Car mCar;
+    private CarAppFocusManager mCarAppFocusManager;
 
     public static class VirtualDisplay {
         public final int mDisplayId;
@@ -96,7 +100,7 @@
         }
     };
 
-    private ServiceConnection mServiceConnection = new ServiceConnection() {
+    private ServiceConnection mClusterRenderingServiceConnection = new ServiceConnection() {
         @Override
         public void onServiceConnected(ComponentName name, IBinder service) {
             Log.i(TAG, "onServiceConnected, name: " + name + ", service: " + service);
@@ -117,6 +121,32 @@
         }
     };
 
+    private ServiceConnection mCarServiceConnection = new ServiceConnection() {
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            try {
+                Log.i(TAG, "onServiceConnected, name: " + name + ", service: " + service);
+                mCarAppFocusManager = (CarAppFocusManager) mCar.getCarManager(
+                        Car.APP_FOCUS_SERVICE);
+                if (mCarAppFocusManager == null) {
+                    Log.e(TAG, "onServiceConnected: unable to obtain CarAppFocusManager");
+                    return;
+                }
+                mCarAppFocusManager.addFocusListener((appType, active) -> {
+                    onNavigationFocusChanged(active);
+                }, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+            } catch (CarNotConnectedException e) {
+                Log.e(TAG, "onServiceConnected: error obtaining manager", e);
+            }
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            Log.i(TAG, "onServiceDisconnected, name: " + name);
+            mCarAppFocusManager = null;
+        }
+    };
+
     private static class MessageHandler extends Handler {
         private final WeakReference<MainClusterActivity> mActivity;
 
@@ -138,7 +168,7 @@
                         data.setClassLoader(ParcelUtils.class.getClassLoader());
                         NavigationState navState = NavigationState
                                 .fromParcelable(data.getParcelable(
-                                        SampleClusterServiceImpl.NAV_STATE_BUNDLE_KEY));
+                                        ClusterRenderingServiceImpl.NAV_STATE_BUNDLE_KEY));
                         mActivity.get().onNavigationStateChange(navState);
                     }
                     break;
@@ -156,9 +186,9 @@
 
         mInputMethodManager = getSystemService(InputMethodManager.class);
 
-        Intent intent = new Intent(this, SampleClusterServiceImpl.class);
+        Intent intent = new Intent(this, ClusterRenderingServiceImpl.class);
         intent.setAction(LOCAL_BINDING_ACTION);
-        bindService(intent, mServiceConnection, 0);
+        bindService(intent, mClusterRenderingServiceConnection, 0);
 
         registerFacets(
                 new Facet<>(findViewById(R.id.btn_nav), 0, NavigationFragment.class),
@@ -169,19 +199,23 @@
         mPager = findViewById(R.id.pager);
         mPager.setAdapter(new ClusterPageAdapter(getSupportFragmentManager()));
         mOrderToFacet.get(0).button.requestFocus();
-        mNavStateController = new NavStateController(findViewById(R.id.maneuver),
-                findViewById(R.id.distance), findViewById(R.id.segment));
+        mNavStateController = new NavStateController(findViewById(R.id.navigation_state));
+
+        mCar = Car.createCar(this, mCarServiceConnection);
+        mCar.connect();
     }
 
     @Override
     protected void onDestroy() {
         super.onDestroy();
         Log.d(TAG, "onDestroy");
+        mCar.disconnect();
+        mCarAppFocusManager = null;
         if (mService != null) {
             sendServiceMessage(MSG_UNREGISTER_CLIENT, null, mServiceCallbacks);
             mService = null;
         }
-        unbindService(mServiceConnection);
+        unbindService(mClusterRenderingServiceConnection);
     }
 
     private void onKeyEvent(KeyEvent event) {
@@ -201,6 +235,12 @@
         }
     }
 
+    private void onNavigationFocusChanged(boolean active) {
+        if (mNavStateController != null) {
+            mNavStateController.setActive(active);
+        }
+    }
+
     public void updateNavDisplay(VirtualDisplay virtualDisplay) {
         if (mService == null) {
             // Service is not bound yet. Hold the information and notify when the service is bound.
@@ -223,8 +263,8 @@
     }
 
     /**
-     * Sends a message to the {@link SampleClusterServiceImpl}, which runs on a different process.
-
+     * Sends a message to the {@link ClusterRenderingServiceImpl}, which runs on a different
+     * process.
      * @param what action to perform
      * @param data action data
      * @param replyTo {@link Messenger} where to reply back
diff --git a/tests/DirectRenderingClusterSample/src/android/car/cluster/sample/NavStateController.java b/tests/DirectRenderingClusterSample/src/android/car/cluster/sample/NavStateController.java
index b45a806..44b6268 100644
--- a/tests/DirectRenderingClusterSample/src/android/car/cluster/sample/NavStateController.java
+++ b/tests/DirectRenderingClusterSample/src/android/car/cluster/sample/NavStateController.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.graphics.drawable.Drawable;
 import android.util.Log;
+import android.view.View;
 import android.widget.ImageView;
 import android.widget.TextView;
 
@@ -36,21 +37,21 @@
     private ImageView mManeuver;
     private TextView mDistance;
     private TextView mSegment;
+    private View mNavigationState;
     private Context mContext;
 
     /**
      * Creates a controller to coordinate updates to the views displaying navigation state
      * data.
      *
-     * @param maneuver {@link ImageView} used to display the immediate navigation maneuver
-     * @param distance {@link TextView} displaying distance to the maneuver
-     * @param segment {@link TextView} displaying the current street.
+     * @param container {@link View} containing the navigation state views
      */
-    public NavStateController(ImageView maneuver, TextView distance, TextView segment) {
-        mManeuver = maneuver;
-        mDistance = distance;
-        mSegment = segment;
-        mContext = maneuver.getContext();
+    public NavStateController(View container) {
+        mNavigationState = container;
+        mManeuver = container.findViewById(R.id.maneuver);
+        mDistance = container.findViewById(R.id.distance);
+        mSegment = container.findViewById(R.id.segment);
+        mContext = container.getContext();
     }
 
     /**
@@ -63,6 +64,18 @@
         mDistance.setText(formatDistance(step != null ? step.getDistance() : null));
     }
 
+    /**
+     * Updates whether turn-by-turn display is active or not. Turn-by-turn would be active whenever
+     * a navigation application has focus.
+     */
+    public void setActive(boolean active) {
+        Log.i(TAG, "Navigation status active: " + active);
+        if (!active) {
+            mManeuver.setImageDrawable(null);
+            mDistance.setText(null);
+        }
+    }
+
     private Drawable getManeuverIcon(@Nullable Maneuver maneuver) {
         if (maneuver == null) {
             return null;