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 package android.car.cluster.sample; 17 18 import static android.car.cluster.CarInstrumentClusterManager.CATEGORY_NAVIGATION; 19 20 import android.app.ActivityOptions; 21 import android.car.CarNotConnectedException; 22 import android.car.cluster.ClusterActivityState; 23 import android.graphics.Rect; 24 import android.hardware.display.DisplayManager; 25 import android.hardware.display.DisplayManager.DisplayListener; 26 import android.hardware.display.VirtualDisplay; 27 import android.os.Bundle; 28 import android.os.Handler; 29 import android.support.v4.app.Fragment; 30 import android.util.Log; 31 import android.view.Display; 32 import android.view.LayoutInflater; 33 import android.view.Surface; 34 import android.view.SurfaceHolder; 35 import android.view.SurfaceHolder.Callback; 36 import android.view.SurfaceView; 37 import android.view.View; 38 import android.view.ViewGroup; 39 40 public class NavigationFragment extends Fragment { 41 private final static String TAG = "Cluster.NavigationFragment"; 42 43 private SurfaceView mSurfaceView; 44 private DisplayManager mDisplayManager; 45 private Rect mUnobscuredBounds; 46 47 // Static because we want to keep alive this virtual display when navigating through 48 // ViewPager (this fragment gets dynamically destroyed and created) 49 private static VirtualDisplay mVirtualDisplay; 50 private static int mRegisteredNavDisplayId = Display.INVALID_DISPLAY; 51 NavigationFragment()52 public NavigationFragment() { 53 // Required empty public constructor 54 } 55 56 private final DisplayListener mDisplayListener = new DisplayListener() { 57 @Override 58 public void onDisplayAdded(int displayId) { 59 int navDisplayId = getVirtualDisplayId(); 60 Log.i(TAG, "onDisplayAdded, displayId: " + displayId 61 + ", navigation display id: " + navDisplayId); 62 63 if (navDisplayId == displayId) { 64 try { 65 getService().setClusterActivityLaunchOptions( 66 CATEGORY_NAVIGATION, 67 ActivityOptions.makeBasic() 68 .setLaunchDisplayId(displayId)); 69 mRegisteredNavDisplayId = displayId; 70 71 getService().setClusterActivityState( 72 CATEGORY_NAVIGATION, 73 ClusterActivityState.create(true, mUnobscuredBounds).toBundle()); 74 } catch (CarNotConnectedException e) { 75 throw new IllegalStateException( 76 "Failed to report nav activity cluster launch options", e); 77 } 78 } 79 } 80 81 @Override 82 public void onDisplayRemoved(int displayId) { 83 if (mRegisteredNavDisplayId == displayId) { 84 try { 85 mRegisteredNavDisplayId = Display.INVALID_DISPLAY; 86 getService().setClusterActivityLaunchOptions( 87 CATEGORY_NAVIGATION, null); 88 } catch (CarNotConnectedException e) { 89 // This can happen only during shutdown, ignore. 90 } 91 } 92 } 93 94 @Override 95 public void onDisplayChanged(int displayId) {} 96 }; 97 98 @Override onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)99 public View onCreateView(LayoutInflater inflater, ViewGroup container, 100 Bundle savedInstanceState) { 101 Log.i(TAG, "onCreateView"); 102 mDisplayManager = getActivity().getSystemService(DisplayManager.class); 103 mDisplayManager.registerDisplayListener(mDisplayListener, new Handler()); 104 105 // Inflate the layout for this fragment 106 View root = inflater.inflate(R.layout.fragment_navigation, container, false); 107 108 mSurfaceView = root.findViewById(R.id.nav_surface); 109 mSurfaceView.getHolder().addCallback(new Callback() { 110 @Override 111 public void surfaceCreated(SurfaceHolder holder) { 112 Log.i(TAG, "surfaceCreated, holder: " + holder); 113 } 114 115 @Override 116 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { 117 Log.i(TAG, "surfaceChanged, holder: " + holder + ", size:" + width + "x" + height 118 + ", format:" + format); 119 120 //Create dummy unobscured area to report to navigation activity. 121 mUnobscuredBounds = new Rect(40, 0, width - 80, height - 40); 122 123 if (mVirtualDisplay == null) { 124 mVirtualDisplay = createVirtualDisplay(holder.getSurface(), width, height); 125 } else { 126 mVirtualDisplay.setSurface(holder.getSurface()); 127 } 128 } 129 130 @Override 131 public void surfaceDestroyed(SurfaceHolder holder) { 132 Log.i(TAG, "surfaceDestroyed, holder: " + holder + ", detaching surface from" 133 + " display, surface: " + holder.getSurface()); 134 // detaching surface is similar to turning off the display 135 mVirtualDisplay.setSurface(null); 136 } 137 }); 138 139 return root; 140 } 141 createVirtualDisplay(Surface surface, int width, int height)142 private VirtualDisplay createVirtualDisplay(Surface surface, int width, int height) { 143 Log.i(TAG, "createVirtualDisplay, surface: " + surface + ", width: " + width 144 + "x" + height); 145 return mDisplayManager.createVirtualDisplay("Cluster-App-VD", width, height, 160, surface, 146 DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY); 147 } 148 149 @Override onDestroy()150 public void onDestroy() { 151 super.onDestroy(); 152 Log.i(TAG, "onDestroy"); 153 } 154 getService()155 private SampleClusterServiceImpl getService() { 156 return ((MainClusterActivity) getActivity()).getService(); 157 } 158 getVirtualDisplayId()159 private int getVirtualDisplayId() { 160 return (mVirtualDisplay != null && mVirtualDisplay.getDisplay() != null) 161 ? mVirtualDisplay.getDisplay().getDisplayId() : Display.INVALID_DISPLAY; 162 } 163 } 164