1 /*
2  * Copyright (C) 2021 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 
17 package com.android.car.cluster.osdouble;
18 
19 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
20 
21 import android.annotation.NonNull;
22 import android.app.ActivityOptions;
23 import android.app.Application;
24 import android.content.ComponentName;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.hardware.display.DisplayManager;
28 import android.os.Handler;
29 import android.os.Looper;
30 import android.text.TextUtils;
31 import android.util.Log;
32 import android.view.Display;
33 import android.view.DisplayAddress;
34 
35 /**
36  * Application class to start ClusterOsDoubleActivity on the physical cluster display.
37  */
38 public class ClusterOsDoubleApplication extends Application {
39     public static final String TAG = "ClusterOsDouble";
40 
41     @Override
onCreate()42     public void onCreate() {
43         super.onCreate();
44         Context context = getApplicationContext();
45         int displayPort = context.getResources().getInteger(R.integer.config_clusterDisplayPort);
46         String displayUniqueId = context.getResources().getString(
47                 R.string.config_clusterDisplayUniqueId);
48 
49         if (displayPort <= 0 && TextUtils.isEmpty(displayUniqueId)) {
50             Log.e(TAG, "Cluster display isn't configured.");
51             return;
52         }
53 
54         DisplayManager displayManager = context.getSystemService(DisplayManager.class);
55         ClusterDisplayMonitor clusterDisplayMonitor = new ClusterDisplayMonitor(context,
56                 displayManager, displayPort, displayUniqueId);
57         clusterDisplayMonitor.start(new Handler(Looper.myLooper()));
58     }
59 
60     /**
61      * Monitors displays and starts the cluster activity when the correct display becomes available.
62      */
63     private static class ClusterDisplayMonitor {
64         private final Context mContext;
65         private final DisplayManager mDisplayManager;
66         private final int mDisplayPort;
67         private final String mDisplayUniqueId;
68 
69         private final DisplayManager.DisplayListener mDisplayListener =
70                 new DisplayManager.DisplayListener() {
71                     @Override
72                     public void onDisplayAdded(int displayId) {
73                         int clusterDisplayId = findClusterDisplayId();
74                         if (clusterDisplayId == displayId) {
75                             Log.d(TAG, "Display " + displayId + " was added. Starting cluster.");
76                             onDisplayReadyForCluster(displayId);
77                         }
78                     }
79 
80                     @Override
81                     public void onDisplayRemoved(int displayId) {
82                         // No-op
83                     }
84 
85                     @Override
86                     public void onDisplayChanged(int displayId) {
87                         // No-op
88                     }
89                 };
90 
ClusterDisplayMonitor(Context context, DisplayManager displayManager, int displayPort, String displayUniqueId)91         public ClusterDisplayMonitor(Context context, DisplayManager displayManager,
92                 int displayPort, String displayUniqueId) {
93             mContext = context;
94             mDisplayManager = displayManager;
95             mDisplayPort = displayPort;
96             mDisplayUniqueId = displayUniqueId;
97         }
98 
start(Handler handler)99         public void start(Handler handler) {
100             int clusterDisplayId = findClusterDisplayId();
101             if (clusterDisplayId != Display.INVALID_DISPLAY) {
102                 onDisplayReadyForCluster(clusterDisplayId);
103             }
104             // This listener will never get unregistered. This is only ok as long as this is a
105             // persistent app that is not expected to stop.
106             mDisplayManager.registerDisplayListener(mDisplayListener, handler);
107         }
108 
onDisplayReadyForCluster(int displayId)109         private void onDisplayReadyForCluster(int displayId) {
110             Intent intent = Intent.makeMainActivity(
111                     ComponentName.createRelative(mContext,
112                             ClusterOsDoubleActivity.class.getName()));
113             intent.addFlags(FLAG_ACTIVITY_NEW_TASK);
114 
115             ActivityOptions options = ActivityOptions.makeBasic().setLaunchDisplayId(displayId);
116             mContext.startActivity(intent, options.toBundle());
117         }
118 
findClusterDisplayId()119         private int findClusterDisplayId() {
120             int displayId = Display.INVALID_DISPLAY;
121             if (mDisplayPort > 0) {
122                 displayId = findDisplayByPort(mDisplayPort);
123                 if (displayId == Display.INVALID_DISPLAY) {
124                     Log.e(TAG, "Can't find the display with portId: " + mDisplayPort);
125                 }
126             } else if (!TextUtils.isEmpty(mDisplayUniqueId)) {
127                 displayId = findDisplayIdByUniqueId(mDisplayUniqueId);
128                 if (displayId == Display.INVALID_DISPLAY) {
129                     Log.e(TAG, "Can't find the display with uniqueId: " + mDisplayUniqueId);
130                 }
131             } else {
132                 // This should not ever happen.
133                 Log.wtf(TAG, "No valid cluster display configs found.");
134             }
135 
136             return displayId;
137         }
138 
findDisplayIdByUniqueId(@onNull String displayUniqueId)139         private int findDisplayIdByUniqueId(@NonNull String displayUniqueId) {
140             for (Display display : mDisplayManager.getDisplays()) {
141                 if (displayUniqueId.equals(display.getUniqueId())) {
142                     return display.getDisplayId();
143                 }
144             }
145             return Display.INVALID_DISPLAY;
146         }
147 
findDisplayByPort(int displayPort)148         private int findDisplayByPort(int displayPort) {
149             for (Display display : mDisplayManager.getDisplays()) {
150                 DisplayAddress address = display.getAddress();
151                 if (!(address instanceof DisplayAddress.Physical)) {
152                     continue;
153                 }
154                 DisplayAddress.Physical physical = (DisplayAddress.Physical) address;
155                 if (physical.getPort() == displayPort) {
156                     return display.getDisplayId();
157                 }
158             }
159             return Display.INVALID_DISPLAY;
160         }
161     }
162 
163 }
164