Loading packages/SystemUI/src/com/android/keyguard/CarrierTextController.java +14 −6 Original line number Diff line number Diff line Loading @@ -21,12 +21,15 @@ import android.content.Intent; import android.content.IntentFilter; import android.net.ConnectivityManager; import android.net.wifi.WifiManager; import android.os.Handler; import android.telephony.ServiceState; import android.telephony.SubscriptionInfo; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Log; import androidx.annotation.VisibleForTesting; import com.android.internal.telephony.IccCardConstants; import com.android.internal.telephony.TelephonyIntents; import com.android.settingslib.WirelessUtils; Loading Loading @@ -206,6 +209,7 @@ public class CarrierTextController { protected void updateCarrierText() { boolean allSimsMissing = true; boolean anySimReadyAndInService = false; boolean missingSimsWithSubs = false; CharSequence displayText = null; List<SubscriptionInfo> subs = mKeyguardUpdateMonitor.getSubscriptionInfo(false); Loading Loading @@ -252,6 +256,7 @@ public class CarrierTextController { // described above. displayText = makeCarrierStringOnEmergencyCapable( getMissingSimMessage(), subs.get(0).getCarrierName()); missingSimsWithSubs = true; } else { // We don't have a SubscriptionInfo to get the emergency calls only from. // Grab it from the old sticky broadcast if possible instead. We can use it Loading Loading @@ -288,12 +293,14 @@ public class CarrierTextController { displayText = getAirplaneModeMessage(); } if (mCarrierTextCallback != null) { mCarrierTextCallback.updateCarrierInfo(new CarrierTextCallbackInfo( Handler handler = Dependency.get(Dependency.MAIN_HANDLER); final CarrierTextCallbackInfo info = new CarrierTextCallbackInfo( displayText, displayText.toString().split(mSeparator.toString()), anySimReadyAndInService, subsIds)); anySimReadyAndInService && !missingSimsWithSubs, subsIds); if (mCarrierTextCallback != null) { handler.post(() -> mCarrierTextCallback.updateCarrierInfo(info)); } } Loading Loading @@ -487,7 +494,8 @@ public class CarrierTextController { public final boolean anySimReady; public final int[] subscriptionIds; CarrierTextCallbackInfo(CharSequence carrierText, CharSequence[] listOfCarriers, @VisibleForTesting public CarrierTextCallbackInfo(CharSequence carrierText, CharSequence[] listOfCarriers, boolean anySimReady, int[] subscriptionIds) { this.carrierText = carrierText; this.listOfCarriers = listOfCarriers; Loading packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java +60 −16 Original line number Diff line number Diff line Loading @@ -54,6 +54,7 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.settingslib.Utils; import com.android.settingslib.drawable.UserIconDrawable; import com.android.settingslib.graph.SignalDrawable; import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.R.dimen; import com.android.systemui.plugins.ActivityStarter; Loading Loading @@ -134,6 +135,15 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, mDeviceProvisionedController = deviceProvisionedController; } @VisibleForTesting public QSFooterImpl(Context context, AttributeSet attrs) { this(context, attrs, Dependency.get(ActivityStarter.class), Dependency.get(UserInfoController.class), Dependency.get(NetworkController.class), Dependency.get(DeviceProvisionedController.class)); } @Override protected void onFinishInflate() { super.onFinishInflate(); Loading Loading @@ -476,12 +486,28 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, mInfos[0].visible && mInfos[1].visible ? View.VISIBLE : View.GONE); } @VisibleForTesting protected int getSlotIndex(int subscriptionId) { return SubscriptionManager.getSlotIndex(subscriptionId); } @Override public void updateCarrierInfo(CarrierTextController.CarrierTextCallbackInfo info) { if (info.anySimReady) { boolean[] slotSeen = new boolean[SIM_SLOTS]; if (info.listOfCarriers.length == info.subscriptionIds.length) { for (int i = 0; i < SIM_SLOTS && i < info.listOfCarriers.length; i++) { int slot = SubscriptionManager.getSlotIndex(info.subscriptionIds[i]); int slot = getSlotIndex(info.subscriptionIds[i]); if (slot >= SIM_SLOTS) { Log.w(TAG, "updateInfoCarrier - slot: " + slot); continue; } if (slot == SubscriptionManager.INVALID_SIM_SLOT_INDEX) { Log.e(TAG, "Invalid SIM slot index for subscription: " + info.subscriptionIds[i]); continue; } mInfos[slot].visible = true; slotSeen[slot] = true; mCarrierTexts[slot].setText(info.listOfCarriers[i].toString().trim()); Loading @@ -493,16 +519,30 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, mCarrierGroups[i].setVisibility(View.GONE); } } handleUpdateState(); } else { // If there are sims ready but there are not the same number of carrier names as // subscription ids, just show the full text in the first slot mInfos[0].visible = true; mCarrierTexts[0].setText(info.carrierText); mCarrierGroups[0].setVisibility(View.VISIBLE); for (int i = 1; i < SIM_SLOTS; i++) { mInfos[i].visible = false; mCarrierTexts[i].setText(""); mCarrierGroups[i].setVisibility(View.GONE); } } } else { mInfos[0].visible = false; mInfos[1].visible = false; mCarrierTexts[0].setText(info.carrierText); mCarrierGroups[0].setVisibility(View.VISIBLE); mCarrierGroups[1].setVisibility(View.GONE); handleUpdateState(); for (int i = 1; i < SIM_SLOTS; i++) { mInfos[i].visible = false; mCarrierTexts[i].setText(""); mCarrierGroups[i].setVisibility(View.GONE); } } handleUpdateState(); } @Override public void setMobileDataIndicators(NetworkController.IconState statusIcon, Loading @@ -510,9 +550,14 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, int qsType, boolean activityIn, boolean activityOut, String typeContentDescription, String description, boolean isWide, int subId, boolean roaming) { int slotIndex = SubscriptionManager.getSlotIndex(subId); int slotIndex = getSlotIndex(subId); if (slotIndex >= SIM_SLOTS) { Log.e(TAG, "setMobileDataIndicators - slot: " + slotIndex); Log.w(TAG, "setMobileDataIndicators - slot: " + slotIndex); return; } if (slotIndex == SubscriptionManager.INVALID_SIM_SLOT_INDEX) { Log.e(TAG, "Invalid SIM slot index for subscription: " + subId); return; } mInfos[slotIndex].visible = statusIcon.visible; mInfos[slotIndex].mobileSignalIconId = statusIcon.icon; Loading @@ -539,7 +584,6 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, boolean roaming; } /** * TextView that changes its ellipsize value with its visibility. */ Loading packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterImplTest.java +121 −0 Original line number Diff line number Diff line Loading @@ -16,27 +16,35 @@ package com.android.systemui.qs; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.support.test.filters.SmallTest; import android.telephony.SubscriptionManager; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; import android.view.LayoutInflater; import android.view.View; import com.android.keyguard.CarrierTextController.CarrierTextCallbackInfo; import com.android.systemui.R; import com.android.systemui.R.id; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.NetworkController; import com.android.systemui.utils.leaks.LeakCheckedTest; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; @RunWith(AndroidTestingRunner.class) @RunWithLooper Loading Loading @@ -68,4 +76,117 @@ public class QSFooterImplTest extends LeakCheckedTest { // Verify Settings wasn't launched. verify(mActivityStarter, never()).startActivity(any(), anyBoolean()); } @Test // throws no Exception public void testUpdateCarrierText_sameLengts() { QSFooterImpl spiedFooter = Mockito.spy(mFooter); when(spiedFooter.getSlotIndex(anyInt())).thenAnswer( new Answer<Integer>() { @Override public Integer answer(InvocationOnMock invocationOnMock) throws Throwable { return invocationOnMock.getArgument(0); } }); // listOfCarriers length 1, subscriptionIds length 1, anySims false CarrierTextCallbackInfo c1 = new CarrierTextCallbackInfo( "", new CharSequence[]{""}, false, new int[]{0}); spiedFooter.updateCarrierInfo(c1); // listOfCarriers length 1, subscriptionIds length 1, anySims true CarrierTextCallbackInfo c2 = new CarrierTextCallbackInfo( "", new CharSequence[]{""}, true, new int[]{0}); spiedFooter.updateCarrierInfo(c2); // listOfCarriers length 2, subscriptionIds length 2, anySims false CarrierTextCallbackInfo c3 = new CarrierTextCallbackInfo( "", new CharSequence[]{"", ""}, false, new int[]{0, 1}); spiedFooter.updateCarrierInfo(c3); // listOfCarriers length 2, subscriptionIds length 2, anySims true CarrierTextCallbackInfo c4 = new CarrierTextCallbackInfo( "", new CharSequence[]{"", ""}, true, new int[]{0, 1}); spiedFooter.updateCarrierInfo(c4); } @Test // throws no Exception public void testUpdateCarrierText_differentLength() { QSFooterImpl spiedFooter = Mockito.spy(mFooter); when(spiedFooter.getSlotIndex(anyInt())).thenAnswer( new Answer<Integer>() { @Override public Integer answer(InvocationOnMock invocationOnMock) throws Throwable { return invocationOnMock.getArgument(0); } }); // listOfCarriers length 2, subscriptionIds length 1, anySims false CarrierTextCallbackInfo c1 = new CarrierTextCallbackInfo( "", new CharSequence[]{"", ""}, false, new int[]{0}); spiedFooter.updateCarrierInfo(c1); // listOfCarriers length 2, subscriptionIds length 1, anySims true CarrierTextCallbackInfo c2 = new CarrierTextCallbackInfo( "", new CharSequence[]{"", ""}, true, new int[]{0}); spiedFooter.updateCarrierInfo(c2); // listOfCarriers length 1, subscriptionIds length 2, anySims false CarrierTextCallbackInfo c3 = new CarrierTextCallbackInfo( "", new CharSequence[]{""}, false, new int[]{0, 1}); spiedFooter.updateCarrierInfo(c3); // listOfCarriers length 1, subscriptionIds length 2, anySims true CarrierTextCallbackInfo c4 = new CarrierTextCallbackInfo( "", new CharSequence[]{""}, true, new int[]{0, 1}); spiedFooter.updateCarrierInfo(c4); } @Test // throws no Exception public void testUpdateCarrierText_invalidSim() { QSFooterImpl spiedFooter = Mockito.spy(mFooter); when(spiedFooter.getSlotIndex(anyInt())).thenReturn( SubscriptionManager.INVALID_SIM_SLOT_INDEX); CarrierTextCallbackInfo c4 = new CarrierTextCallbackInfo( "", new CharSequence[]{"", ""}, true, new int[]{0, 1}); spiedFooter.updateCarrierInfo(c4); } @Test // throws no Exception public void testSetMobileDataIndicators_invalidSim() { QSFooterImpl spiedFooter = Mockito.spy(mFooter); when(spiedFooter.getSlotIndex(anyInt())).thenReturn( SubscriptionManager.INVALID_SIM_SLOT_INDEX); spiedFooter.setMobileDataIndicators( mock(NetworkController.IconState.class), mock(NetworkController.IconState.class), 0, 0, true, true, "", "", true, 0, true); } } Loading
packages/SystemUI/src/com/android/keyguard/CarrierTextController.java +14 −6 Original line number Diff line number Diff line Loading @@ -21,12 +21,15 @@ import android.content.Intent; import android.content.IntentFilter; import android.net.ConnectivityManager; import android.net.wifi.WifiManager; import android.os.Handler; import android.telephony.ServiceState; import android.telephony.SubscriptionInfo; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Log; import androidx.annotation.VisibleForTesting; import com.android.internal.telephony.IccCardConstants; import com.android.internal.telephony.TelephonyIntents; import com.android.settingslib.WirelessUtils; Loading Loading @@ -206,6 +209,7 @@ public class CarrierTextController { protected void updateCarrierText() { boolean allSimsMissing = true; boolean anySimReadyAndInService = false; boolean missingSimsWithSubs = false; CharSequence displayText = null; List<SubscriptionInfo> subs = mKeyguardUpdateMonitor.getSubscriptionInfo(false); Loading Loading @@ -252,6 +256,7 @@ public class CarrierTextController { // described above. displayText = makeCarrierStringOnEmergencyCapable( getMissingSimMessage(), subs.get(0).getCarrierName()); missingSimsWithSubs = true; } else { // We don't have a SubscriptionInfo to get the emergency calls only from. // Grab it from the old sticky broadcast if possible instead. We can use it Loading Loading @@ -288,12 +293,14 @@ public class CarrierTextController { displayText = getAirplaneModeMessage(); } if (mCarrierTextCallback != null) { mCarrierTextCallback.updateCarrierInfo(new CarrierTextCallbackInfo( Handler handler = Dependency.get(Dependency.MAIN_HANDLER); final CarrierTextCallbackInfo info = new CarrierTextCallbackInfo( displayText, displayText.toString().split(mSeparator.toString()), anySimReadyAndInService, subsIds)); anySimReadyAndInService && !missingSimsWithSubs, subsIds); if (mCarrierTextCallback != null) { handler.post(() -> mCarrierTextCallback.updateCarrierInfo(info)); } } Loading Loading @@ -487,7 +494,8 @@ public class CarrierTextController { public final boolean anySimReady; public final int[] subscriptionIds; CarrierTextCallbackInfo(CharSequence carrierText, CharSequence[] listOfCarriers, @VisibleForTesting public CarrierTextCallbackInfo(CharSequence carrierText, CharSequence[] listOfCarriers, boolean anySimReady, int[] subscriptionIds) { this.carrierText = carrierText; this.listOfCarriers = listOfCarriers; Loading
packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java +60 −16 Original line number Diff line number Diff line Loading @@ -54,6 +54,7 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.settingslib.Utils; import com.android.settingslib.drawable.UserIconDrawable; import com.android.settingslib.graph.SignalDrawable; import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.R.dimen; import com.android.systemui.plugins.ActivityStarter; Loading Loading @@ -134,6 +135,15 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, mDeviceProvisionedController = deviceProvisionedController; } @VisibleForTesting public QSFooterImpl(Context context, AttributeSet attrs) { this(context, attrs, Dependency.get(ActivityStarter.class), Dependency.get(UserInfoController.class), Dependency.get(NetworkController.class), Dependency.get(DeviceProvisionedController.class)); } @Override protected void onFinishInflate() { super.onFinishInflate(); Loading Loading @@ -476,12 +486,28 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, mInfos[0].visible && mInfos[1].visible ? View.VISIBLE : View.GONE); } @VisibleForTesting protected int getSlotIndex(int subscriptionId) { return SubscriptionManager.getSlotIndex(subscriptionId); } @Override public void updateCarrierInfo(CarrierTextController.CarrierTextCallbackInfo info) { if (info.anySimReady) { boolean[] slotSeen = new boolean[SIM_SLOTS]; if (info.listOfCarriers.length == info.subscriptionIds.length) { for (int i = 0; i < SIM_SLOTS && i < info.listOfCarriers.length; i++) { int slot = SubscriptionManager.getSlotIndex(info.subscriptionIds[i]); int slot = getSlotIndex(info.subscriptionIds[i]); if (slot >= SIM_SLOTS) { Log.w(TAG, "updateInfoCarrier - slot: " + slot); continue; } if (slot == SubscriptionManager.INVALID_SIM_SLOT_INDEX) { Log.e(TAG, "Invalid SIM slot index for subscription: " + info.subscriptionIds[i]); continue; } mInfos[slot].visible = true; slotSeen[slot] = true; mCarrierTexts[slot].setText(info.listOfCarriers[i].toString().trim()); Loading @@ -493,16 +519,30 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, mCarrierGroups[i].setVisibility(View.GONE); } } handleUpdateState(); } else { // If there are sims ready but there are not the same number of carrier names as // subscription ids, just show the full text in the first slot mInfos[0].visible = true; mCarrierTexts[0].setText(info.carrierText); mCarrierGroups[0].setVisibility(View.VISIBLE); for (int i = 1; i < SIM_SLOTS; i++) { mInfos[i].visible = false; mCarrierTexts[i].setText(""); mCarrierGroups[i].setVisibility(View.GONE); } } } else { mInfos[0].visible = false; mInfos[1].visible = false; mCarrierTexts[0].setText(info.carrierText); mCarrierGroups[0].setVisibility(View.VISIBLE); mCarrierGroups[1].setVisibility(View.GONE); handleUpdateState(); for (int i = 1; i < SIM_SLOTS; i++) { mInfos[i].visible = false; mCarrierTexts[i].setText(""); mCarrierGroups[i].setVisibility(View.GONE); } } handleUpdateState(); } @Override public void setMobileDataIndicators(NetworkController.IconState statusIcon, Loading @@ -510,9 +550,14 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, int qsType, boolean activityIn, boolean activityOut, String typeContentDescription, String description, boolean isWide, int subId, boolean roaming) { int slotIndex = SubscriptionManager.getSlotIndex(subId); int slotIndex = getSlotIndex(subId); if (slotIndex >= SIM_SLOTS) { Log.e(TAG, "setMobileDataIndicators - slot: " + slotIndex); Log.w(TAG, "setMobileDataIndicators - slot: " + slotIndex); return; } if (slotIndex == SubscriptionManager.INVALID_SIM_SLOT_INDEX) { Log.e(TAG, "Invalid SIM slot index for subscription: " + subId); return; } mInfos[slotIndex].visible = statusIcon.visible; mInfos[slotIndex].mobileSignalIconId = statusIcon.icon; Loading @@ -539,7 +584,6 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, boolean roaming; } /** * TextView that changes its ellipsize value with its visibility. */ Loading
packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterImplTest.java +121 −0 Original line number Diff line number Diff line Loading @@ -16,27 +16,35 @@ package com.android.systemui.qs; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.support.test.filters.SmallTest; import android.telephony.SubscriptionManager; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; import android.view.LayoutInflater; import android.view.View; import com.android.keyguard.CarrierTextController.CarrierTextCallbackInfo; import com.android.systemui.R; import com.android.systemui.R.id; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.NetworkController; import com.android.systemui.utils.leaks.LeakCheckedTest; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; @RunWith(AndroidTestingRunner.class) @RunWithLooper Loading Loading @@ -68,4 +76,117 @@ public class QSFooterImplTest extends LeakCheckedTest { // Verify Settings wasn't launched. verify(mActivityStarter, never()).startActivity(any(), anyBoolean()); } @Test // throws no Exception public void testUpdateCarrierText_sameLengts() { QSFooterImpl spiedFooter = Mockito.spy(mFooter); when(spiedFooter.getSlotIndex(anyInt())).thenAnswer( new Answer<Integer>() { @Override public Integer answer(InvocationOnMock invocationOnMock) throws Throwable { return invocationOnMock.getArgument(0); } }); // listOfCarriers length 1, subscriptionIds length 1, anySims false CarrierTextCallbackInfo c1 = new CarrierTextCallbackInfo( "", new CharSequence[]{""}, false, new int[]{0}); spiedFooter.updateCarrierInfo(c1); // listOfCarriers length 1, subscriptionIds length 1, anySims true CarrierTextCallbackInfo c2 = new CarrierTextCallbackInfo( "", new CharSequence[]{""}, true, new int[]{0}); spiedFooter.updateCarrierInfo(c2); // listOfCarriers length 2, subscriptionIds length 2, anySims false CarrierTextCallbackInfo c3 = new CarrierTextCallbackInfo( "", new CharSequence[]{"", ""}, false, new int[]{0, 1}); spiedFooter.updateCarrierInfo(c3); // listOfCarriers length 2, subscriptionIds length 2, anySims true CarrierTextCallbackInfo c4 = new CarrierTextCallbackInfo( "", new CharSequence[]{"", ""}, true, new int[]{0, 1}); spiedFooter.updateCarrierInfo(c4); } @Test // throws no Exception public void testUpdateCarrierText_differentLength() { QSFooterImpl spiedFooter = Mockito.spy(mFooter); when(spiedFooter.getSlotIndex(anyInt())).thenAnswer( new Answer<Integer>() { @Override public Integer answer(InvocationOnMock invocationOnMock) throws Throwable { return invocationOnMock.getArgument(0); } }); // listOfCarriers length 2, subscriptionIds length 1, anySims false CarrierTextCallbackInfo c1 = new CarrierTextCallbackInfo( "", new CharSequence[]{"", ""}, false, new int[]{0}); spiedFooter.updateCarrierInfo(c1); // listOfCarriers length 2, subscriptionIds length 1, anySims true CarrierTextCallbackInfo c2 = new CarrierTextCallbackInfo( "", new CharSequence[]{"", ""}, true, new int[]{0}); spiedFooter.updateCarrierInfo(c2); // listOfCarriers length 1, subscriptionIds length 2, anySims false CarrierTextCallbackInfo c3 = new CarrierTextCallbackInfo( "", new CharSequence[]{""}, false, new int[]{0, 1}); spiedFooter.updateCarrierInfo(c3); // listOfCarriers length 1, subscriptionIds length 2, anySims true CarrierTextCallbackInfo c4 = new CarrierTextCallbackInfo( "", new CharSequence[]{""}, true, new int[]{0, 1}); spiedFooter.updateCarrierInfo(c4); } @Test // throws no Exception public void testUpdateCarrierText_invalidSim() { QSFooterImpl spiedFooter = Mockito.spy(mFooter); when(spiedFooter.getSlotIndex(anyInt())).thenReturn( SubscriptionManager.INVALID_SIM_SLOT_INDEX); CarrierTextCallbackInfo c4 = new CarrierTextCallbackInfo( "", new CharSequence[]{"", ""}, true, new int[]{0, 1}); spiedFooter.updateCarrierInfo(c4); } @Test // throws no Exception public void testSetMobileDataIndicators_invalidSim() { QSFooterImpl spiedFooter = Mockito.spy(mFooter); when(spiedFooter.getSlotIndex(anyInt())).thenReturn( SubscriptionManager.INVALID_SIM_SLOT_INDEX); spiedFooter.setMobileDataIndicators( mock(NetworkController.IconState.class), mock(NetworkController.IconState.class), 0, 0, true, true, "", "", true, 0, true); } }