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.notification.headsup; 18 19 import android.content.Context; 20 import android.view.LayoutInflater; 21 import android.view.View; 22 import android.view.ViewGroup; 23 import android.view.WindowManager; 24 25 import androidx.annotation.VisibleForTesting; 26 27 import com.android.car.notification.CarNotificationTypeItem; 28 import com.android.car.notification.R; 29 30 import java.util.LinkedList; 31 32 /** 33 * Container for displaying Heads Up Notifications. 34 */ 35 public abstract class CarHeadsUpNotificationContainer { 36 private static final String TAG = "CarHUNContainer"; 37 private final LinkedList<HunImportance> mHunImportanceLinkedList = new LinkedList<>(); 38 private final ViewGroup mHunWindow; 39 private final ViewGroup mHunContent; 40 private final boolean mShowHunOnBottom; 41 CarHeadsUpNotificationContainer(Context context, WindowManager windowManager)42 public CarHeadsUpNotificationContainer(Context context, WindowManager windowManager) { 43 mShowHunOnBottom = context.getResources().getBoolean( 44 R.bool.config_showHeadsUpNotificationOnBottom); 45 mHunWindow = (ViewGroup) LayoutInflater.from(context).inflate( 46 mShowHunOnBottom ? R.layout.headsup_container_bottom 47 : R.layout.headsup_container, /* root= */ null, /* attachToRoot= */ false); 48 mHunContent = mHunWindow.findViewById(R.id.headsup_content); 49 mHunWindow.setVisibility(View.INVISIBLE); 50 windowManager.addView(mHunWindow, getWindowManagerLayoutParams()); 51 } 52 53 /** 54 * @return {@link WindowManager.LayoutParams} to be used when adding HUN Window to {@link 55 * WindowManager}. 56 */ getWindowManagerLayoutParams()57 protected abstract WindowManager.LayoutParams getWindowManagerLayoutParams(); 58 59 /** 60 * Displays a given notification View to the user and inserts the view at Z-index according to 61 * its {@link HunImportance}, 62 */ displayNotification(View notificationView, CarNotificationTypeItem notificationTypeItem)63 public void displayNotification(View notificationView, 64 CarNotificationTypeItem notificationTypeItem) { 65 HunImportance hunImportance = getImportanceForCarNotificationTypeItem(notificationTypeItem); 66 67 displayNotificationInner(notificationView, hunImportance); 68 69 if (shouldShowHunPanel()) { 70 getHunWindow().setVisibility(View.VISIBLE); 71 } 72 } 73 displayNotificationInner(View notificationView, HunImportance hunImportance)74 private void displayNotificationInner(View notificationView, HunImportance hunImportance) { 75 if (mHunImportanceLinkedList.isEmpty() || hunImportance.equals(HunImportance.EMERGENCY)) { 76 mHunImportanceLinkedList.add(hunImportance); 77 getHunContent().addView(notificationView); 78 return; 79 } 80 81 int index = 0; 82 for (; index < mHunImportanceLinkedList.size(); index++) { 83 if (hunImportance.isLessImportantThan(mHunImportanceLinkedList.get(index))) break; 84 } 85 if (index < mHunImportanceLinkedList.size()) { 86 mHunImportanceLinkedList.add(index, hunImportance); 87 getHunContent().addView(notificationView, index); 88 return; 89 } 90 91 mHunImportanceLinkedList.add(hunImportance); 92 getHunContent().addView(notificationView); 93 } 94 95 /** 96 * @return {@code true} if Hun panel should be set as visible after displaying HUN. 97 */ shouldShowHunPanel()98 public boolean shouldShowHunPanel() { 99 return !isVisible(); 100 } 101 102 /** 103 * Removes a given notification View from the container. 104 */ removeNotification(View notificationView)105 public void removeNotification(View notificationView) { 106 if (getHunContent().getChildCount() == 0) return; 107 108 int index = getHunContent().indexOfChild(notificationView); 109 if (index == -1) return; 110 111 getHunContent().removeViewAt(index); 112 mHunImportanceLinkedList.remove(index); 113 114 if (shouldHideHunPanel()) { 115 getHunWindow().setVisibility(View.INVISIBLE); 116 } 117 } 118 119 /** 120 * @return {@code true} if HUN panel should be set as invisible after removing a HUN. 121 */ shouldHideHunPanel()122 public boolean shouldHideHunPanel() { 123 return getHunContent().getChildCount() == 0; 124 } 125 126 /** 127 * @return Whether or not the container is currently visible. 128 */ isVisible()129 public final boolean isVisible() { 130 return getHunWindow().getVisibility() == View.VISIBLE; 131 } 132 133 /** 134 * @return HUN window. 135 */ getHunWindow()136 protected final ViewGroup getHunWindow() { 137 return mHunWindow; 138 } 139 140 /** 141 * @return HUN content inside of window. 142 */ getHunContent()143 protected final ViewGroup getHunContent() { 144 return mHunContent; 145 } 146 147 /** 148 * @return {@code true} if HUN should be shown on bottom. 149 */ getShowHunOnBottom()150 protected final boolean getShowHunOnBottom() { 151 return mShowHunOnBottom; 152 } 153 getImportanceForCarNotificationTypeItem( CarNotificationTypeItem notificationTypeItem)154 private HunImportance getImportanceForCarNotificationTypeItem( 155 CarNotificationTypeItem notificationTypeItem) { 156 if (notificationTypeItem == CarNotificationTypeItem.EMERGENCY) { 157 return HunImportance.EMERGENCY; 158 } else if (notificationTypeItem == CarNotificationTypeItem.WARNING) { 159 return HunImportance.WARNING; 160 } else if (notificationTypeItem == CarNotificationTypeItem.NAVIGATION) { 161 return HunImportance.NAVIGATION; 162 } else if (notificationTypeItem == CarNotificationTypeItem.CALL) { 163 return HunImportance.CALL; 164 } else { 165 return HunImportance.OTHER; 166 } 167 } 168 169 @VisibleForTesting 170 enum HunImportance { 171 OTHER(/* level= */ 0), 172 CALL(/* level= */ 1), 173 NAVIGATION(/* level= */ 2), 174 WARNING(/* level= */ 3), 175 EMERGENCY(/* level= */ 4); 176 177 private final Integer mLevel; 178 HunImportance(int level)179 HunImportance(int level) { 180 this.mLevel = level; 181 } 182 isMoreImportantThan(HunImportance other)183 boolean isMoreImportantThan(HunImportance other) { 184 return this.mLevel > other.mLevel; 185 } 186 isLessImportantThan(HunImportance other)187 boolean isLessImportantThan(HunImportance other) { 188 return this.mLevel < other.mLevel; 189 } 190 } 191 } 192