• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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