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