13985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney/* 23985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney * Copyright (C) 2017 The Android Open Source Project 33985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney * 43985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney * Licensed under the Apache License, Version 2.0 (the "License"); 53985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney * you may not use this file except in compliance with the License. 63985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney * You may obtain a copy of the License at 73985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney * 83985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney * http://www.apache.org/licenses/LICENSE-2.0 93985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney * 103985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney * Unless required by applicable law or agreed to in writing, software 113985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney * distributed under the License is distributed on an "AS IS" BASIS, 123985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 133985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney * See the License for the specific language governing permissions and 143985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney * limitations under the License 153985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney */ 163985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtneypackage com.android.systemui.statusbar; 173985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney 183985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtneyimport android.content.Context; 193985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtneyimport android.os.Handler; 203985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtneyimport android.os.RemoteException; 213985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtneyimport android.os.ServiceManager; 223985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtneyimport android.os.SystemClock; 233985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtneyimport android.service.notification.NotificationListenerService; 243985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtneyimport android.util.ArraySet; 253985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtneyimport android.util.Log; 263985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney 273985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtneyimport com.android.internal.annotations.VisibleForTesting; 283985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtneyimport com.android.internal.statusbar.IStatusBarService; 293985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtneyimport com.android.internal.statusbar.NotificationVisibility; 306c313d3224c878d832db3ed833f4a3dd3786fb1fEliot Courtneyimport com.android.systemui.Dependency; 313985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtneyimport com.android.systemui.UiOffloadThread; 323985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney 333985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtneyimport java.util.ArrayList; 343985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtneyimport java.util.Collection; 353985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtneyimport java.util.Collections; 363985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney 373985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney/** 383985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney * Handles notification logging, in particular, logging which notifications are visible and which 393985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney * are not. 403985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney */ 413985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtneypublic class NotificationLogger { 423985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney private static final String TAG = "NotificationLogger"; 433985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney 443985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney /** The minimum delay in ms between reports of notification visibility. */ 453985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney private static final int VISIBILITY_REPORT_MIN_DELAY_MS = 500; 463985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney 473985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney /** Keys of notifications currently visible to the user. */ 483985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney private final ArraySet<NotificationVisibility> mCurrentlyVisibleNotifications = 493985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney new ArraySet<>(); 506c313d3224c878d832db3ed833f4a3dd3786fb1fEliot Courtney 516c313d3224c878d832db3ed833f4a3dd3786fb1fEliot Courtney // Dependencies: 526c313d3224c878d832db3ed833f4a3dd3786fb1fEliot Courtney private final NotificationListenerService mNotificationListener = 536c313d3224c878d832db3ed833f4a3dd3786fb1fEliot Courtney Dependency.get(NotificationListener.class); 546c313d3224c878d832db3ed833f4a3dd3786fb1fEliot Courtney private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class); 553985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney 564a96b36fd9857cd3d3534ed0396ec3d7155a324cEliot Courtney protected NotificationEntryManager mEntryManager; 573985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney protected Handler mHandler = new Handler(); 583985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney protected IStatusBarService mBarService; 593985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney private long mLastVisibilityReportUptimeMs; 602b4c3a08fcfcb60c527a1a372318e5ef4ce2d49cEliot Courtney private NotificationListContainer mListContainer; 613985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney 622b4c3a08fcfcb60c527a1a372318e5ef4ce2d49cEliot Courtney protected final OnChildLocationsChangedListener mNotificationLocationsChangedListener = 632b4c3a08fcfcb60c527a1a372318e5ef4ce2d49cEliot Courtney new OnChildLocationsChangedListener() { 643985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney @Override 652b4c3a08fcfcb60c527a1a372318e5ef4ce2d49cEliot Courtney public void onChildLocationsChanged() { 663985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney if (mHandler.hasCallbacks(mVisibilityReporter)) { 673985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney // Visibilities will be reported when the existing 683985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney // callback is executed. 693985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney return; 703985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney } 713985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney // Calculate when we're allowed to run the visibility 723985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney // reporter. Note that this timestamp might already have 733985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney // passed. That's OK, the callback will just be executed 743985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney // ASAP. 753985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney long nextReportUptimeMs = 763985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney mLastVisibilityReportUptimeMs + VISIBILITY_REPORT_MIN_DELAY_MS; 773985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney mHandler.postAtTime(mVisibilityReporter, nextReportUptimeMs); 783985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney } 793985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney }; 803985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney 813985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney // Tracks notifications currently visible in mNotificationStackScroller and 823985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney // emits visibility events via NoMan on changes. 833985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney protected final Runnable mVisibilityReporter = new Runnable() { 843985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney private final ArraySet<NotificationVisibility> mTmpNewlyVisibleNotifications = 853985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney new ArraySet<>(); 863985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney private final ArraySet<NotificationVisibility> mTmpCurrentlyVisibleNotifications = 873985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney new ArraySet<>(); 883985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney private final ArraySet<NotificationVisibility> mTmpNoLongerVisibleNotifications = 893985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney new ArraySet<>(); 903985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney 913985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney @Override 923985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney public void run() { 933985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney mLastVisibilityReportUptimeMs = SystemClock.uptimeMillis(); 943985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney 953985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney // 1. Loop over mNotificationData entries: 963985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney // A. Keep list of visible notifications. 973985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney // B. Keep list of previously hidden, now visible notifications. 983985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney // 2. Compute no-longer visible notifications by removing currently 993985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney // visible notifications from the set of previously visible 1003985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney // notifications. 1013985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney // 3. Report newly visible and no-longer visible notifications. 1023985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney // 4. Keep currently visible notifications for next report. 1034a96b36fd9857cd3d3534ed0396ec3d7155a324cEliot Courtney ArrayList<NotificationData.Entry> activeNotifications = mEntryManager 1044a96b36fd9857cd3d3534ed0396ec3d7155a324cEliot Courtney .getNotificationData().getActiveNotifications(); 1053985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney int N = activeNotifications.size(); 1063985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney for (int i = 0; i < N; i++) { 1073985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney NotificationData.Entry entry = activeNotifications.get(i); 1083985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney String key = entry.notification.getKey(); 1092b4c3a08fcfcb60c527a1a372318e5ef4ce2d49cEliot Courtney boolean isVisible = mListContainer.isInVisibleLocation(entry.row); 110d39f0d52dcdca78fb8d57fa0a805ec0bdc8589daDieter Hsu NotificationVisibility visObj = NotificationVisibility.obtain(key, i, N, isVisible); 1113985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney boolean previouslyVisible = mCurrentlyVisibleNotifications.contains(visObj); 1123985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney if (isVisible) { 1133985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney // Build new set of visible notifications. 1143985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney mTmpCurrentlyVisibleNotifications.add(visObj); 1153985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney if (!previouslyVisible) { 1163985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney mTmpNewlyVisibleNotifications.add(visObj); 1173985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney } 1183985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney } else { 1193985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney // release object 1203985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney visObj.recycle(); 1213985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney } 1223985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney } 1233985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney mTmpNoLongerVisibleNotifications.addAll(mCurrentlyVisibleNotifications); 1243985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney mTmpNoLongerVisibleNotifications.removeAll(mTmpCurrentlyVisibleNotifications); 1253985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney 1263985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney logNotificationVisibilityChanges( 1273985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney mTmpNewlyVisibleNotifications, mTmpNoLongerVisibleNotifications); 1283985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney 1293985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney recycleAllVisibilityObjects(mCurrentlyVisibleNotifications); 1303985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney mCurrentlyVisibleNotifications.addAll(mTmpCurrentlyVisibleNotifications); 1313985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney 1323985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney recycleAllVisibilityObjects(mTmpNoLongerVisibleNotifications); 1333985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney mTmpCurrentlyVisibleNotifications.clear(); 1343985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney mTmpNewlyVisibleNotifications.clear(); 1353985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney mTmpNoLongerVisibleNotifications.clear(); 1363985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney } 1373985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney }; 1383985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney 1396c313d3224c878d832db3ed833f4a3dd3786fb1fEliot Courtney public NotificationLogger() { 1403985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney mBarService = IStatusBarService.Stub.asInterface( 1413985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney ServiceManager.getService(Context.STATUS_BAR_SERVICE)); 1423985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney } 1433985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney 1444a96b36fd9857cd3d3534ed0396ec3d7155a324cEliot Courtney public void setUpWithEntryManager(NotificationEntryManager entryManager, 1452b4c3a08fcfcb60c527a1a372318e5ef4ce2d49cEliot Courtney NotificationListContainer listContainer) { 1464a96b36fd9857cd3d3534ed0396ec3d7155a324cEliot Courtney mEntryManager = entryManager; 1472b4c3a08fcfcb60c527a1a372318e5ef4ce2d49cEliot Courtney mListContainer = listContainer; 1483985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney } 1493985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney 1503985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney public void stopNotificationLogging() { 1513985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney // Report all notifications as invisible and turn down the 1523985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney // reporter. 1533985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney if (!mCurrentlyVisibleNotifications.isEmpty()) { 1543985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney logNotificationVisibilityChanges( 1553985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney Collections.emptyList(), mCurrentlyVisibleNotifications); 1563985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney recycleAllVisibilityObjects(mCurrentlyVisibleNotifications); 1573985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney } 1583985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney mHandler.removeCallbacks(mVisibilityReporter); 1592b4c3a08fcfcb60c527a1a372318e5ef4ce2d49cEliot Courtney mListContainer.setChildLocationsChangedListener(null); 1603985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney } 1613985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney 1623985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney public void startNotificationLogging() { 1632b4c3a08fcfcb60c527a1a372318e5ef4ce2d49cEliot Courtney mListContainer.setChildLocationsChangedListener(mNotificationLocationsChangedListener); 1643985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney // Some transitions like mVisibleToUser=false -> mVisibleToUser=true don't 1653985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney // cause the scroller to emit child location events. Hence generate 1663985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney // one ourselves to guarantee that we're reporting visible 1673985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney // notifications. 1683985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney // (Note that in cases where the scroller does emit events, this 1693985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney // additional event doesn't break anything.) 1702b4c3a08fcfcb60c527a1a372318e5ef4ce2d49cEliot Courtney mNotificationLocationsChangedListener.onChildLocationsChanged(); 1713985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney } 1723985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney 1733985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney private void logNotificationVisibilityChanges( 1743985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney Collection<NotificationVisibility> newlyVisible, 1753985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney Collection<NotificationVisibility> noLongerVisible) { 1763985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney if (newlyVisible.isEmpty() && noLongerVisible.isEmpty()) { 1773985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney return; 1783985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney } 1792e89e8d893acfe571ad6f5555baccb1b5e55abb7Chris Wren final NotificationVisibility[] newlyVisibleAr = cloneVisibilitiesAsArr(newlyVisible); 1802e89e8d893acfe571ad6f5555baccb1b5e55abb7Chris Wren final NotificationVisibility[] noLongerVisibleAr = cloneVisibilitiesAsArr(noLongerVisible); 1812e89e8d893acfe571ad6f5555baccb1b5e55abb7Chris Wren 1823985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney mUiOffloadThread.submit(() -> { 1833985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney try { 1843985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney mBarService.onNotificationVisibilityChanged(newlyVisibleAr, noLongerVisibleAr); 1853985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney } catch (RemoteException e) { 1863985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney // Ignore. 1873985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney } 1883985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney 1893985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney final int N = newlyVisible.size(); 1903985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney if (N > 0) { 1913985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney String[] newlyVisibleKeyAr = new String[N]; 1923985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney for (int i = 0; i < N; i++) { 1933985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney newlyVisibleKeyAr[i] = newlyVisibleAr[i].key; 1943985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney } 1953985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney 1963985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney // TODO: Call NotificationEntryManager to do this, once it exists. 1973985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney // TODO: Consider not catching all runtime exceptions here. 1983985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney try { 1993985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney mNotificationListener.setNotificationsShown(newlyVisibleKeyAr); 2003985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney } catch (RuntimeException e) { 2013985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney Log.d(TAG, "failed setNotificationsShown: ", e); 2023985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney } 2033985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney } 2042e89e8d893acfe571ad6f5555baccb1b5e55abb7Chris Wren recycleAllVisibilityObjects(newlyVisibleAr); 2052e89e8d893acfe571ad6f5555baccb1b5e55abb7Chris Wren recycleAllVisibilityObjects(noLongerVisibleAr); 2063985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney }); 2073985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney } 2083985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney 2093985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney private void recycleAllVisibilityObjects(ArraySet<NotificationVisibility> array) { 2103985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney final int N = array.size(); 2113985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney for (int i = 0 ; i < N; i++) { 2123985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney array.valueAt(i).recycle(); 2133985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney } 2143985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney array.clear(); 2153985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney } 2163985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney 2172e89e8d893acfe571ad6f5555baccb1b5e55abb7Chris Wren private void recycleAllVisibilityObjects(NotificationVisibility[] array) { 2182e89e8d893acfe571ad6f5555baccb1b5e55abb7Chris Wren final int N = array.length; 2192e89e8d893acfe571ad6f5555baccb1b5e55abb7Chris Wren for (int i = 0 ; i < N; i++) { 2202e89e8d893acfe571ad6f5555baccb1b5e55abb7Chris Wren if (array[i] != null) { 2212e89e8d893acfe571ad6f5555baccb1b5e55abb7Chris Wren array[i].recycle(); 2222e89e8d893acfe571ad6f5555baccb1b5e55abb7Chris Wren } 2232e89e8d893acfe571ad6f5555baccb1b5e55abb7Chris Wren } 2242e89e8d893acfe571ad6f5555baccb1b5e55abb7Chris Wren } 2252e89e8d893acfe571ad6f5555baccb1b5e55abb7Chris Wren 2262e89e8d893acfe571ad6f5555baccb1b5e55abb7Chris Wren private NotificationVisibility[] cloneVisibilitiesAsArr(Collection<NotificationVisibility> c) { 2272e89e8d893acfe571ad6f5555baccb1b5e55abb7Chris Wren 2282e89e8d893acfe571ad6f5555baccb1b5e55abb7Chris Wren final NotificationVisibility[] array = new NotificationVisibility[c.size()]; 2292e89e8d893acfe571ad6f5555baccb1b5e55abb7Chris Wren int i = 0; 2302e89e8d893acfe571ad6f5555baccb1b5e55abb7Chris Wren for(NotificationVisibility nv: c) { 2312e89e8d893acfe571ad6f5555baccb1b5e55abb7Chris Wren if (nv != null) { 2322e89e8d893acfe571ad6f5555baccb1b5e55abb7Chris Wren array[i] = nv.clone(); 2332e89e8d893acfe571ad6f5555baccb1b5e55abb7Chris Wren } 2342e89e8d893acfe571ad6f5555baccb1b5e55abb7Chris Wren i++; 2352e89e8d893acfe571ad6f5555baccb1b5e55abb7Chris Wren } 2362e89e8d893acfe571ad6f5555baccb1b5e55abb7Chris Wren return array; 2372e89e8d893acfe571ad6f5555baccb1b5e55abb7Chris Wren } 2382e89e8d893acfe571ad6f5555baccb1b5e55abb7Chris Wren 2393985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney @VisibleForTesting 2403985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney public Runnable getVisibilityReporter() { 2413985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney return mVisibilityReporter; 2423985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney } 2432b4c3a08fcfcb60c527a1a372318e5ef4ce2d49cEliot Courtney 2442b4c3a08fcfcb60c527a1a372318e5ef4ce2d49cEliot Courtney /** 2452b4c3a08fcfcb60c527a1a372318e5ef4ce2d49cEliot Courtney * A listener that is notified when some child locations might have changed. 2462b4c3a08fcfcb60c527a1a372318e5ef4ce2d49cEliot Courtney */ 2472b4c3a08fcfcb60c527a1a372318e5ef4ce2d49cEliot Courtney public interface OnChildLocationsChangedListener { 2482b4c3a08fcfcb60c527a1a372318e5ef4ce2d49cEliot Courtney void onChildLocationsChanged(); 2492b4c3a08fcfcb60c527a1a372318e5ef4ce2d49cEliot Courtney } 2503985ad5773cd4573525cbfb00e132b960a83ef48Eliot Courtney} 251