1 /* 2 * Copyright (C) 2015 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.systemui.statusbar; 18 19 import com.android.internal.util.Preconditions; 20 import com.android.systemui.statusbar.phone.StatusBarWindowManager; 21 import com.android.systemui.statusbar.policy.HeadsUpManager; 22 import com.android.systemui.statusbar.policy.RemoteInputView; 23 24 import android.util.ArraySet; 25 26 import java.lang.ref.WeakReference; 27 import java.util.ArrayList; 28 29 /** 30 * Keeps track of the currently active {@link RemoteInputView}s. 31 */ 32 public class RemoteInputController { 33 34 private final ArrayList<WeakReference<NotificationData.Entry>> mOpen = new ArrayList<>(); 35 private final ArraySet<String> mSpinning = new ArraySet<>(); 36 private final ArrayList<Callback> mCallbacks = new ArrayList<>(3); 37 private final HeadsUpManager mHeadsUpManager; 38 39 public RemoteInputController(StatusBarWindowManager sbwm, HeadsUpManager headsUpManager) { 40 addCallback(sbwm); 41 mHeadsUpManager = headsUpManager; 42 } 43 44 public void addRemoteInput(NotificationData.Entry entry) { 45 Preconditions.checkNotNull(entry); 46 47 boolean found = pruneWeakThenRemoveAndContains( 48 entry /* contains */, null /* remove */); 49 if (!found) { 50 mOpen.add(new WeakReference<>(entry)); 51 } 52 53 apply(entry); 54 } 55 56 public void removeRemoteInput(NotificationData.Entry entry) { 57 Preconditions.checkNotNull(entry); 58 59 pruneWeakThenRemoveAndContains(null /* contains */, entry /* remove */); 60 61 apply(entry); 62 } 63 64 public void addSpinning(String key) { 65 mSpinning.add(key); 66 } 67 68 public void removeSpinning(String key) { 69 mSpinning.remove(key); 70 } 71 72 public boolean isSpinning(String key) { 73 return mSpinning.contains(key); 74 } 75 76 private void apply(NotificationData.Entry entry) { 77 mHeadsUpManager.setRemoteInputActive(entry, isRemoteInputActive(entry)); 78 boolean remoteInputActive = isRemoteInputActive(); 79 int N = mCallbacks.size(); 80 for (int i = 0; i < N; i++) { 81 mCallbacks.get(i).onRemoteInputActive(remoteInputActive); 82 } 83 } 84 85 /** 86 * @return true if {@param entry} has an active RemoteInput 87 */ 88 public boolean isRemoteInputActive(NotificationData.Entry entry) { 89 return pruneWeakThenRemoveAndContains(entry /* contains */, null /* remove */); 90 } 91 92 /** 93 * @return true if any entry has an active RemoteInput 94 */ 95 public boolean isRemoteInputActive() { 96 pruneWeakThenRemoveAndContains(null /* contains */, null /* remove */); 97 return !mOpen.isEmpty(); 98 } 99 100 /** 101 * Prunes dangling weak references, removes entries referring to {@param remove} and returns 102 * whether {@param contains} is part of the array in a single loop. 103 * @param remove if non-null, removes this entry from the active remote inputs 104 * @return true if {@param contains} is in the set of active remote inputs 105 */ 106 private boolean pruneWeakThenRemoveAndContains( 107 NotificationData.Entry contains, NotificationData.Entry remove) { 108 boolean found = false; 109 for (int i = mOpen.size() - 1; i >= 0; i--) { 110 NotificationData.Entry item = mOpen.get(i).get(); 111 if (item == null || item == remove) { 112 mOpen.remove(i); 113 } else if (item == contains) { 114 found = true; 115 } 116 } 117 return found; 118 } 119 120 121 public void addCallback(Callback callback) { 122 Preconditions.checkNotNull(callback); 123 mCallbacks.add(callback); 124 } 125 126 public void remoteInputSent(NotificationData.Entry entry) { 127 int N = mCallbacks.size(); 128 for (int i = 0; i < N; i++) { 129 mCallbacks.get(i).onRemoteInputSent(entry); 130 } 131 } 132 133 public void closeRemoteInputs() { 134 if (mOpen.size() == 0) { 135 return; 136 } 137 138 // Make a copy because closing the remote inputs will modify mOpen. 139 ArrayList<NotificationData.Entry> list = new ArrayList<>(mOpen.size()); 140 for (int i = mOpen.size() - 1; i >= 0; i--) { 141 NotificationData.Entry item = mOpen.get(i).get(); 142 if (item != null && item.row != null) { 143 list.add(item); 144 } 145 } 146 147 for (int i = list.size() - 1; i >= 0; i--) { 148 NotificationData.Entry item = list.get(i); 149 if (item.row != null) { 150 item.row.closeRemoteInput(); 151 } 152 } 153 } 154 155 public interface Callback { 156 default void onRemoteInputActive(boolean active) {} 157 158 default void onRemoteInputSent(NotificationData.Entry entry) {} 159 } 160 } 161