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