18c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk/* 28c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk * Copyright (C) 2017 The Android Open Source Project 38c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk * 48c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 58c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk * except in compliance with the License. You may obtain a copy of the License at 68c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk * 78c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk * http://www.apache.org/licenses/LICENSE-2.0 88c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk * 98c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk * Unless required by applicable law or agreed to in writing, software distributed under the 108c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 118c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk * KIND, either express or implied. See the License for the specific language governing 128c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk * permissions and limitations under the License. 138c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk */ 148c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk 158c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monkpackage com.android.systemui.qs.tileimpl; 168c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk 178c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monkimport static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_QS_CLICK; 188c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monkimport static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_QS_LONG_PRESS; 198c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monkimport static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_QS_SECONDARY_CLICK; 208c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monkimport static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_QS_POSITION; 218c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monkimport static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_QS_VALUE; 228c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monkimport static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_ACTION; 238c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk 242ef8ed65324ba7b7fd89d7d9c45e357ec00b98c4Jason Monkimport static org.mockito.ArgumentMatchers.any; 258c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monkimport static org.mockito.ArgumentMatchers.anyInt; 268c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monkimport static org.mockito.ArgumentMatchers.eq; 278c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monkimport static org.mockito.Matchers.argThat; 281c6116cbbb0c8000b576443e4b13026b1d8529d9Jason Monkimport static org.mockito.Mockito.clearInvocations; 298c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monkimport static org.mockito.Mockito.mock; 301c6116cbbb0c8000b576443e4b13026b1d8529d9Jason Monkimport static org.mockito.Mockito.never; 311c6116cbbb0c8000b576443e4b13026b1d8529d9Jason Monkimport static org.mockito.Mockito.spy; 328c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monkimport static org.mockito.Mockito.verify; 338c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monkimport static org.mockito.Mockito.when; 348c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk 351c6116cbbb0c8000b576443e4b13026b1d8529d9Jason Monkimport static java.lang.Thread.sleep; 361c6116cbbb0c8000b576443e4b13026b1d8529d9Jason Monk 378c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monkimport android.content.Intent; 388c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monkimport android.metrics.LogMaker; 39fba8faf5c5f58f115fa3c099da9ef0953b484147Jason Monkimport android.support.test.filters.SmallTest; 40db2cfa36097e01a94c75d11c913424816c92b447Rohan Shahimport android.support.test.InstrumentationRegistry; 418c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monkimport android.testing.AndroidTestingRunner; 428c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monkimport android.testing.TestableLooper; 438c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monkimport android.testing.TestableLooper.RunWithLooper; 448c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk 458c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monkimport com.android.internal.logging.MetricsLogger; 468c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monkimport com.android.systemui.Dependency; 478c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monkimport com.android.systemui.SysuiTestCase; 488c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monkimport com.android.systemui.plugins.qs.QSTile; 498c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monkimport com.android.systemui.qs.QSHost; 508c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monkimport com.android.systemui.qs.QSTileHost; 518c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk 528c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monkimport org.junit.Before; 53659a91beed628ca6887f4756622c77b38b55b50fGeoffrey Pitschimport org.junit.Ignore; 548c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monkimport org.junit.Test; 558c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monkimport org.junit.runner.RunWith; 568c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monkimport org.mockito.ArgumentMatcher; 578c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk 588c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk@RunWith(AndroidTestingRunner.class) 598c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk@RunWithLooper 60fba8faf5c5f58f115fa3c099da9ef0953b484147Jason Monk@SmallTest 618c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monkpublic class QSTileImplTest extends SysuiTestCase { 628c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk 638c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk public static final int POSITION = 14; 648c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk private TestableLooper mTestableLooper; 658c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk private TileImpl mTile; 668c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk private QSTileHost mHost; 678c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk private MetricsLogger mMetricsLogger; 688c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk 698c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk @Before 708c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk public void setup() throws Exception { 718c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk String spec = "spec"; 728c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk mTestableLooper = TestableLooper.get(this); 738c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper()); 748c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class); 758c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk mHost = mock(QSTileHost.class); 768c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk when(mHost.indexOf(spec)).thenReturn(POSITION); 77db2cfa36097e01a94c75d11c913424816c92b447Rohan Shah when(mHost.getContext()).thenReturn(mContext.getBaseContext()); 781c6116cbbb0c8000b576443e4b13026b1d8529d9Jason Monk 791c6116cbbb0c8000b576443e4b13026b1d8529d9Jason Monk mTile = spy(new TileImpl(mHost)); 801c6116cbbb0c8000b576443e4b13026b1d8529d9Jason Monk mTile.mHandler = mTile.new H(mTestableLooper.getLooper()); 811c6116cbbb0c8000b576443e4b13026b1d8529d9Jason Monk mTile.setTileSpec(spec); 828c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk } 838c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk 848c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk @Test 858c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk public void testClick_Metrics() { 868c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk mTile.click(); 878c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk verify(mMetricsLogger).write(argThat(new TileLogMatcher(ACTION_QS_CLICK))); 888c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk } 898c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk 908c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk @Test 918c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk public void testSecondaryClick_Metrics() { 928c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk mTile.secondaryClick(); 938c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk verify(mMetricsLogger).write(argThat(new TileLogMatcher(ACTION_QS_SECONDARY_CLICK))); 948c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk } 958c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk 968c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk @Test 978c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk public void testLongClick_Metrics() { 988c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk mTile.longClick(); 998c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk verify(mMetricsLogger).write(argThat(new TileLogMatcher(ACTION_QS_LONG_PRESS))); 1008c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk } 1018c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk 1028c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk @Test 1038c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk public void testPopulate() { 1048c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk LogMaker maker = mock(LogMaker.class); 1058c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk when(maker.setSubtype(anyInt())).thenReturn(maker); 1062ef8ed65324ba7b7fd89d7d9c45e357ec00b98c4Jason Monk when(maker.addTaggedData(anyInt(), any())).thenReturn(maker); 1078c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk mTile.getState().value = true; 1088c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk mTile.populate(maker); 1098c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk verify(maker).addTaggedData(eq(FIELD_QS_VALUE), eq(1)); 1108c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk verify(maker).addTaggedData(eq(FIELD_QS_POSITION), eq(POSITION)); 1118c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk } 1128c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk 1131c6116cbbb0c8000b576443e4b13026b1d8529d9Jason Monk @Test 114659a91beed628ca6887f4756622c77b38b55b50fGeoffrey Pitsch @Ignore("flaky") 1151c6116cbbb0c8000b576443e4b13026b1d8529d9Jason Monk public void testStaleTimeout() throws InterruptedException { 1161c6116cbbb0c8000b576443e4b13026b1d8529d9Jason Monk when(mTile.getStaleTimeout()).thenReturn(5l); 1171c6116cbbb0c8000b576443e4b13026b1d8529d9Jason Monk clearInvocations(mTile); 1181c6116cbbb0c8000b576443e4b13026b1d8529d9Jason Monk 1191c6116cbbb0c8000b576443e4b13026b1d8529d9Jason Monk mTile.handleRefreshState(null); 1201c6116cbbb0c8000b576443e4b13026b1d8529d9Jason Monk mTestableLooper.processAllMessages(); 1211c6116cbbb0c8000b576443e4b13026b1d8529d9Jason Monk verify(mTile, never()).handleStale(); 1221c6116cbbb0c8000b576443e4b13026b1d8529d9Jason Monk 1231c6116cbbb0c8000b576443e4b13026b1d8529d9Jason Monk sleep(10); 1241c6116cbbb0c8000b576443e4b13026b1d8529d9Jason Monk mTestableLooper.processAllMessages(); 1251c6116cbbb0c8000b576443e4b13026b1d8529d9Jason Monk verify(mTile).handleStale(); 1261c6116cbbb0c8000b576443e4b13026b1d8529d9Jason Monk } 1271c6116cbbb0c8000b576443e4b13026b1d8529d9Jason Monk 1281c6116cbbb0c8000b576443e4b13026b1d8529d9Jason Monk @Test 1291c6116cbbb0c8000b576443e4b13026b1d8529d9Jason Monk public void testStaleListening() { 1301c6116cbbb0c8000b576443e4b13026b1d8529d9Jason Monk mTile.handleStale(); 1311c6116cbbb0c8000b576443e4b13026b1d8529d9Jason Monk mTestableLooper.processAllMessages(); 1321c6116cbbb0c8000b576443e4b13026b1d8529d9Jason Monk verify(mTile).handleSetListening(eq(true)); 1331c6116cbbb0c8000b576443e4b13026b1d8529d9Jason Monk 1341c6116cbbb0c8000b576443e4b13026b1d8529d9Jason Monk mTile.handleRefreshState(null); 1351c6116cbbb0c8000b576443e4b13026b1d8529d9Jason Monk mTestableLooper.processAllMessages(); 1361c6116cbbb0c8000b576443e4b13026b1d8529d9Jason Monk verify(mTile).handleSetListening(eq(false)); 1371c6116cbbb0c8000b576443e4b13026b1d8529d9Jason Monk } 1381c6116cbbb0c8000b576443e4b13026b1d8529d9Jason Monk 1398c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk private class TileLogMatcher implements ArgumentMatcher<LogMaker> { 1408c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk 1418c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk private final int mCategory; 1428c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk public String mInvalid; 1438c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk 1448c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk public TileLogMatcher(int category) { 1458c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk mCategory = category; 1468c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk } 1478c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk 1488c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk @Override 1498c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk public boolean matches(LogMaker arg) { 1508c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk if (arg.getCategory() != mCategory) { 1518c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk mInvalid = "Expected category " + mCategory + " but was " + arg.getCategory(); 1528c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk return false; 1538c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk } 1548c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk if (arg.getType() != TYPE_ACTION) { 1558c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk mInvalid = "Expected type " + TYPE_ACTION + " but was " + arg.getType(); 1568c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk return false; 1578c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk } 1588c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk if (arg.getSubtype() != mTile.getMetricsCategory()) { 1598c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk mInvalid = "Expected subtype " + mTile.getMetricsCategory() + " but was " 1608c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk + arg.getSubtype(); 1618c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk return false; 1628c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk } 1638c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk return true; 1648c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk } 1658c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk 1668c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk @Override 1678c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk public String toString() { 1688c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk return mInvalid; 1698c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk } 1708c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk } 1718c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk 1728c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk private static class TileImpl extends QSTileImpl<QSTile.BooleanState> { 1738c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk protected TileImpl(QSHost host) { 1748c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk super(host); 1758c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk } 1768c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk 1778c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk @Override 1788c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk public BooleanState newTileState() { 1798c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk return new BooleanState(); 1808c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk } 1818c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk 1828c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk @Override 1838c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk protected void handleClick() { 1848c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk 1858c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk } 1868c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk 1878c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk @Override 1888c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk protected void handleUpdateState(BooleanState state, Object arg) { 1898c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk 1908c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk } 1918c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk 1928c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk @Override 1938c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk public int getMetricsCategory() { 1948c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk return 42; 1958c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk } 1968c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk 1978c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk @Override 1988c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk public Intent getLongClickIntent() { 1998c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk return null; 2008c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk } 2018c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk 2028c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk @Override 2031c6116cbbb0c8000b576443e4b13026b1d8529d9Jason Monk protected void handleSetListening(boolean listening) { 2048c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk 2058c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk } 2068c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk 2078c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk @Override 2088c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk public CharSequence getTileLabel() { 2098c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk return null; 2108c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk } 2118c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk } 2128c09ac7435c62a8ba2e34d57a7ac3be2ddf23adcJason Monk} 213