1 /* 2 * Copyright (C) 2020 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.ui.paintbooth; 18 19 import android.app.Notification; 20 import android.app.NotificationChannel; 21 import android.app.NotificationManager; 22 import android.app.PendingIntent; 23 import android.app.Service; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.graphics.PixelFormat; 27 import android.graphics.Point; 28 import android.os.IBinder; 29 import android.util.DisplayMetrics; 30 import android.view.Display; 31 import android.view.Gravity; 32 import android.view.LayoutInflater; 33 import android.view.View; 34 import android.view.WindowManager; 35 36 import androidx.core.app.NotificationCompat; 37 38 /** 39 * To start the service: 40 * adb shell am start-foreground-service com.android.car.ui.paintbooth/.DisplayService 41 * 42 * To stop the service: 43 * adb shell am stopservice com.android.car.ui.paintbooth/.DisplayService 44 * 45 * When the service is started it will draw a overlay view on top of the screen displayed. This 46 * overlay comes from a SVG file that can be modified to take different shapes. This service will be 47 * used to display different screen styles from OEMs. 48 */ 49 public class VisibleBoundsSimulator extends Service { 50 private static final int FOREGROUND_SERVICE_ID = 222; 51 private View mContainer; 52 53 private WindowManager mWindowManager; 54 55 @Override onBind(Intent intent)56 public IBinder onBind(Intent intent) { 57 throw new UnsupportedOperationException("Not yet implemented."); 58 } 59 60 @Override onStartCommand(Intent intent, int flags, int startId)61 public int onStartCommand(Intent intent, int flags, int startId) { 62 if (MainActivity.STOP_SERVICE.equals(intent.getAction())) { 63 stopSelf(); 64 } 65 66 return START_STICKY; 67 } 68 69 @Override onCreate()70 public void onCreate() { 71 72 Intent notificationIntent = new Intent(this, VisibleBoundsSimulator.class); 73 74 PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, 75 notificationIntent, PendingIntent.FLAG_IMMUTABLE); 76 77 NotificationChannel channel = new NotificationChannel("DisplayService", 78 "Show overlay screen", 79 NotificationManager.IMPORTANCE_DEFAULT); 80 NotificationManager notificationManager = 81 (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); 82 notificationManager.createNotificationChannel(channel); 83 84 Notification notification = 85 new NotificationCompat.Builder(this, "DisplayService") 86 .setSmallIcon(R.drawable.ic_launcher) 87 .setContentTitle("DisplayService") 88 .setContentText("Show overlay screen") 89 .setContentIntent(pendingIntent).build(); 90 91 startForeground(FOREGROUND_SERVICE_ID, notification); 92 applyDisplayOverlay(); 93 } 94 95 /** 96 * Creates a view overlay on top of a new window. The overlay gravity is set to left and 97 * bottom. If the width and height is not provided then the default is to take up the entire 98 * screen. Overlay will show bounds around the view and we can still click through the window. 99 */ applyDisplayOverlay()100 private void applyDisplayOverlay() { 101 mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE); 102 103 DisplayMetrics displayMetrics = new DisplayMetrics(); 104 105 mWindowManager.getDefaultDisplay().getRealMetrics(displayMetrics); 106 int screenHeight = displayMetrics.heightPixels; 107 int screenWidth = displayMetrics.widthPixels; 108 LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE); 109 final WindowManager.LayoutParams params = new WindowManager.LayoutParams( 110 WindowManager.LayoutParams.WRAP_CONTENT, 111 WindowManager.LayoutParams.WRAP_CONTENT, 112 WindowManager.LayoutParams.TYPE_SYSTEM_ALERT, 113 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 114 | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, 115 PixelFormat.TRANSLUCENT); 116 117 params.packageName = this.getPackageName(); 118 params.gravity = Gravity.BOTTOM | Gravity.LEFT; 119 120 Display display = mWindowManager.getDefaultDisplay(); 121 Point size = new Point(); 122 display.getSize(size); 123 int height = size.y; 124 125 params.x = 0; 126 // If the sysUI is showing and nav bar is taking up some space at the bottom we want to 127 // offset the height of the navBar so that the overlay starts from the bottom left. 128 params.y = -(screenHeight - height); 129 130 float overlayWidth = getApplicationContext().getResources().getDimension( 131 R.dimen.screen_shape_container_width); 132 float overlayHeight = getApplicationContext().getResources().getDimension( 133 R.dimen.screen_shape_container_height); 134 135 136 params.width = (int) (overlayWidth == 0 ? screenWidth : overlayHeight); 137 params.height = (int) (overlayHeight == 0 ? screenHeight : overlayHeight); 138 params.setTitle("Simulated display bound"); 139 140 mContainer = inflater.inflate(R.layout.simulated_screen_shape_container, null); 141 mContainer.setLayoutParams(params); 142 143 mWindowManager.addView(mContainer, params); 144 } 145 146 @Override onDestroy()147 public void onDestroy() { 148 super.onDestroy(); 149 mWindowManager.removeView(mContainer); 150 stopSelf(); 151 } 152 } 153