11c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos/* 21c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos * Copyright (C) 2015 The Android Open Source Project 31c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos * 41c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos * Licensed under the Apache License, Version 2.0 (the "License"); 51c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos * you may not use this file except in compliance with the License. 61c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos * You may obtain a copy of the License at 71c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos * 81c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos * http://www.apache.org/licenses/LICENSE-2.0 91c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos * 101c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos * Unless required by applicable law or agreed to in writing, software 111c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos * distributed under the License is distributed on an "AS IS" BASIS, 121c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 131c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos * See the License for the specific language governing permissions and 141c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos * limitations under the License 151c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos */ 161c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos 171c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roospackage com.android.systemui.statusbar; 181c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos 191c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roosimport com.android.internal.util.Preconditions; 201c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roosimport com.android.systemui.statusbar.phone.StatusBarWindowManager; 211c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roosimport com.android.systemui.statusbar.policy.HeadsUpManager; 221c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roosimport com.android.systemui.statusbar.policy.RemoteInputView; 231c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos 24c0a579e5752b38257c31acb2f0b5b08447355205Adrian Roosimport android.util.ArraySet; 25c0a579e5752b38257c31acb2f0b5b08447355205Adrian Roos 261c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roosimport java.lang.ref.WeakReference; 271c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roosimport java.util.ArrayList; 281c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos 291c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos/** 301c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos * Keeps track of the currently active {@link RemoteInputView}s. 311c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos */ 321c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roospublic class RemoteInputController { 331c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos 34c0a579e5752b38257c31acb2f0b5b08447355205Adrian Roos private final ArrayList<WeakReference<NotificationData.Entry>> mOpen = new ArrayList<>(); 35c0a579e5752b38257c31acb2f0b5b08447355205Adrian Roos private final ArraySet<String> mSpinning = new ArraySet<>(); 36d28ccd7cab8e1a187dfd6c5733efe7cdfb652202Adrian Roos private final ArrayList<Callback> mCallbacks = new ArrayList<>(3); 371c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos private final HeadsUpManager mHeadsUpManager; 381c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos 391c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos public RemoteInputController(StatusBarWindowManager sbwm, HeadsUpManager headsUpManager) { 40d28ccd7cab8e1a187dfd6c5733efe7cdfb652202Adrian Roos addCallback(sbwm); 411c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos mHeadsUpManager = headsUpManager; 421c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos } 431c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos 441c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos public void addRemoteInput(NotificationData.Entry entry) { 451c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos Preconditions.checkNotNull(entry); 461c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos 471c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos boolean found = pruneWeakThenRemoveAndContains( 481c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos entry /* contains */, null /* remove */); 491c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos if (!found) { 50c0a579e5752b38257c31acb2f0b5b08447355205Adrian Roos mOpen.add(new WeakReference<>(entry)); 511c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos } 521c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos 531c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos apply(entry); 541c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos } 551c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos 561c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos public void removeRemoteInput(NotificationData.Entry entry) { 571c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos Preconditions.checkNotNull(entry); 581c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos 591c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos pruneWeakThenRemoveAndContains(null /* contains */, entry /* remove */); 601c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos 611c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos apply(entry); 621c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos } 631c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos 64c0a579e5752b38257c31acb2f0b5b08447355205Adrian Roos public void addSpinning(String key) { 65c0a579e5752b38257c31acb2f0b5b08447355205Adrian Roos mSpinning.add(key); 66c0a579e5752b38257c31acb2f0b5b08447355205Adrian Roos } 67c0a579e5752b38257c31acb2f0b5b08447355205Adrian Roos 68c0a579e5752b38257c31acb2f0b5b08447355205Adrian Roos public void removeSpinning(String key) { 69c0a579e5752b38257c31acb2f0b5b08447355205Adrian Roos mSpinning.remove(key); 70c0a579e5752b38257c31acb2f0b5b08447355205Adrian Roos } 71c0a579e5752b38257c31acb2f0b5b08447355205Adrian Roos 72c0a579e5752b38257c31acb2f0b5b08447355205Adrian Roos public boolean isSpinning(String key) { 73c0a579e5752b38257c31acb2f0b5b08447355205Adrian Roos return mSpinning.contains(key); 74c0a579e5752b38257c31acb2f0b5b08447355205Adrian Roos } 75c0a579e5752b38257c31acb2f0b5b08447355205Adrian Roos 761c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos private void apply(NotificationData.Entry entry) { 771c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos mHeadsUpManager.setRemoteInputActive(entry, isRemoteInputActive(entry)); 78d28ccd7cab8e1a187dfd6c5733efe7cdfb652202Adrian Roos boolean remoteInputActive = isRemoteInputActive(); 79d28ccd7cab8e1a187dfd6c5733efe7cdfb652202Adrian Roos int N = mCallbacks.size(); 80d28ccd7cab8e1a187dfd6c5733efe7cdfb652202Adrian Roos for (int i = 0; i < N; i++) { 81d28ccd7cab8e1a187dfd6c5733efe7cdfb652202Adrian Roos mCallbacks.get(i).onRemoteInputActive(remoteInputActive); 82d28ccd7cab8e1a187dfd6c5733efe7cdfb652202Adrian Roos } 831c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos } 841c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos 851c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos /** 861c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos * @return true if {@param entry} has an active RemoteInput 871c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos */ 881c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos public boolean isRemoteInputActive(NotificationData.Entry entry) { 891c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos return pruneWeakThenRemoveAndContains(entry /* contains */, null /* remove */); 901c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos } 911c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos 921c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos /** 931c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos * @return true if any entry has an active RemoteInput 941c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos */ 951c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos public boolean isRemoteInputActive() { 961c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos pruneWeakThenRemoveAndContains(null /* contains */, null /* remove */); 97c0a579e5752b38257c31acb2f0b5b08447355205Adrian Roos return !mOpen.isEmpty(); 981c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos } 991c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos 1001c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos /** 1011c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos * Prunes dangling weak references, removes entries referring to {@param remove} and returns 1021c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos * whether {@param contains} is part of the array in a single loop. 1031c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos * @param remove if non-null, removes this entry from the active remote inputs 1041c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos * @return true if {@param contains} is in the set of active remote inputs 1051c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos */ 1061c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos private boolean pruneWeakThenRemoveAndContains( 1071c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos NotificationData.Entry contains, NotificationData.Entry remove) { 1081c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos boolean found = false; 109c0a579e5752b38257c31acb2f0b5b08447355205Adrian Roos for (int i = mOpen.size() - 1; i >= 0; i--) { 110c0a579e5752b38257c31acb2f0b5b08447355205Adrian Roos NotificationData.Entry item = mOpen.get(i).get(); 1111c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos if (item == null || item == remove) { 112c0a579e5752b38257c31acb2f0b5b08447355205Adrian Roos mOpen.remove(i); 1131c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos } else if (item == contains) { 1141c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos found = true; 1151c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos } 1161c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos } 1171c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos return found; 1181c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos } 1191c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos 1201c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos 121d28ccd7cab8e1a187dfd6c5733efe7cdfb652202Adrian Roos public void addCallback(Callback callback) { 122d28ccd7cab8e1a187dfd6c5733efe7cdfb652202Adrian Roos Preconditions.checkNotNull(callback); 123d28ccd7cab8e1a187dfd6c5733efe7cdfb652202Adrian Roos mCallbacks.add(callback); 124d28ccd7cab8e1a187dfd6c5733efe7cdfb652202Adrian Roos } 125d28ccd7cab8e1a187dfd6c5733efe7cdfb652202Adrian Roos 126c0a579e5752b38257c31acb2f0b5b08447355205Adrian Roos public void remoteInputSent(NotificationData.Entry entry) { 127c0a579e5752b38257c31acb2f0b5b08447355205Adrian Roos int N = mCallbacks.size(); 128c0a579e5752b38257c31acb2f0b5b08447355205Adrian Roos for (int i = 0; i < N; i++) { 129c0a579e5752b38257c31acb2f0b5b08447355205Adrian Roos mCallbacks.get(i).onRemoteInputSent(entry); 130c0a579e5752b38257c31acb2f0b5b08447355205Adrian Roos } 131c0a579e5752b38257c31acb2f0b5b08447355205Adrian Roos } 132c0a579e5752b38257c31acb2f0b5b08447355205Adrian Roos 13307215350f97c46b3c74d0d8757ec12ea61ced258Adrian Roos public void closeRemoteInputs() { 13407215350f97c46b3c74d0d8757ec12ea61ced258Adrian Roos if (mOpen.size() == 0) { 13507215350f97c46b3c74d0d8757ec12ea61ced258Adrian Roos return; 13607215350f97c46b3c74d0d8757ec12ea61ced258Adrian Roos } 13707215350f97c46b3c74d0d8757ec12ea61ced258Adrian Roos 13807215350f97c46b3c74d0d8757ec12ea61ced258Adrian Roos // Make a copy because closing the remote inputs will modify mOpen. 13907215350f97c46b3c74d0d8757ec12ea61ced258Adrian Roos ArrayList<NotificationData.Entry> list = new ArrayList<>(mOpen.size()); 14007215350f97c46b3c74d0d8757ec12ea61ced258Adrian Roos for (int i = mOpen.size() - 1; i >= 0; i--) { 14107215350f97c46b3c74d0d8757ec12ea61ced258Adrian Roos NotificationData.Entry item = mOpen.get(i).get(); 14207215350f97c46b3c74d0d8757ec12ea61ced258Adrian Roos if (item != null && item.row != null) { 14307215350f97c46b3c74d0d8757ec12ea61ced258Adrian Roos list.add(item); 14407215350f97c46b3c74d0d8757ec12ea61ced258Adrian Roos } 14507215350f97c46b3c74d0d8757ec12ea61ced258Adrian Roos } 14607215350f97c46b3c74d0d8757ec12ea61ced258Adrian Roos 14707215350f97c46b3c74d0d8757ec12ea61ced258Adrian Roos for (int i = list.size() - 1; i >= 0; i--) { 14807215350f97c46b3c74d0d8757ec12ea61ced258Adrian Roos NotificationData.Entry item = list.get(i); 14907215350f97c46b3c74d0d8757ec12ea61ced258Adrian Roos if (item.row != null) { 15007215350f97c46b3c74d0d8757ec12ea61ced258Adrian Roos item.row.closeRemoteInput(); 15107215350f97c46b3c74d0d8757ec12ea61ced258Adrian Roos } 15207215350f97c46b3c74d0d8757ec12ea61ced258Adrian Roos } 15307215350f97c46b3c74d0d8757ec12ea61ced258Adrian Roos } 15407215350f97c46b3c74d0d8757ec12ea61ced258Adrian Roos 155d28ccd7cab8e1a187dfd6c5733efe7cdfb652202Adrian Roos public interface Callback { 156c0a579e5752b38257c31acb2f0b5b08447355205Adrian Roos default void onRemoteInputActive(boolean active) {} 157c0a579e5752b38257c31acb2f0b5b08447355205Adrian Roos 158c0a579e5752b38257c31acb2f0b5b08447355205Adrian Roos default void onRemoteInputSent(NotificationData.Entry entry) {} 159d28ccd7cab8e1a187dfd6c5733efe7cdfb652202Adrian Roos } 1601c0ca508b9d6184beba2050e6ab3a44ac5be137aAdrian Roos} 161