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 */
16package android.car.cluster.sample;
17
18import static android.car.cluster.CarInstrumentClusterManager.CATEGORY_NAVIGATION;
19
20import android.app.ActivityOptions;
21import android.car.CarNotConnectedException;
22import android.car.cluster.ClusterActivityState;
23import android.graphics.Rect;
24import android.hardware.display.DisplayManager;
25import android.hardware.display.DisplayManager.DisplayListener;
26import android.hardware.display.VirtualDisplay;
27import android.os.Bundle;
28import android.os.Handler;
29import android.support.v4.app.Fragment;
30import android.util.Log;
31import android.view.Display;
32import android.view.LayoutInflater;
33import android.view.Surface;
34import android.view.SurfaceHolder;
35import android.view.SurfaceHolder.Callback;
36import android.view.SurfaceView;
37import android.view.View;
38import android.view.ViewGroup;
39
40public 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
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
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
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
150    public void onDestroy() {
151        super.onDestroy();
152        Log.i(TAG, "onDestroy");
153    }
154
155    private SampleClusterServiceImpl getService() {
156        return ((MainClusterActivity) getActivity()).getService();
157    }
158
159    private int getVirtualDisplayId() {
160        return (mVirtualDisplay != null && mVirtualDisplay.getDisplay() != null)
161                ? mVirtualDisplay.getDisplay().getDisplayId() : Display.INVALID_DISPLAY;
162    }
163}
164