From a9831d40d081cc8c7ca32891e64c09b5e1d19abb Mon Sep 17 00:00:00 2001 From: Philip Quinn Date: Mon, 13 Feb 2017 20:07:09 -0800 Subject: [PATCH] Add settings for the assist gesture. Availability of the gesture is gated by a new FeatureProvider. Test: make -j RunSettingsRoboTests; manual test on supported/unsupported configurations. Change-Id: I3529367a73e33370d5112b91d5144293ffa7fa22 --- res/drawable-nodpi/gesture_assist | 0 res/raw/gesture_assist.mp4 | 0 res/values/strings.xml | 6 ++ res/xml/assist_gesture_settings.xml | 32 ++++++++ res/xml/gesture_settings.xml | 9 +- res/xml/language_and_input.xml | 7 +- res/xml/manage_assist.xml | 5 ++ .../settings/applications/ManageAssist.java | 16 ++++ .../settings/core/gateway/SettingsGateway.java | 2 + .../gestures/AssistGestureFeatureProvider.java | 27 ++++++ .../gestures/AssistGestureFeatureProviderImpl.java | 28 +++++++ .../AssistGesturePreferenceController.java | 69 ++++++++++++++++ .../settings/gestures/AssistGestureSettings.java | 74 +++++++++++++++++ .../android/settings/gestures/GestureSettings.java | 2 + .../language/LanguageAndInputSettings.java | 3 +- .../android/settings/overlay/FeatureFactory.java | 3 + .../settings/overlay/FeatureFactoryImpl.java | 11 +++ .../settings/search/SearchIndexableResources.java | 2 + .../AssistGesturePreferenceControllerTest.java | 95 ++++++++++++++++++++++ .../gestures/AssistGestureSettingsTest.java | 83 +++++++++++++++++++ .../search/DatabaseIndexingManagerTest.java | 6 +- .../android/settings/search/XmlParserUtilTest.java | 6 +- .../settings/testutils/FakeFeatureFactory.java | 8 ++ 23 files changed, 485 insertions(+), 9 deletions(-) create mode 100644 res/drawable-nodpi/gesture_assist create mode 100644 res/raw/gesture_assist.mp4 create mode 100644 res/xml/assist_gesture_settings.xml create mode 100644 src/com/android/settings/gestures/AssistGestureFeatureProvider.java create mode 100644 src/com/android/settings/gestures/AssistGestureFeatureProviderImpl.java create mode 100644 src/com/android/settings/gestures/AssistGesturePreferenceController.java create mode 100644 src/com/android/settings/gestures/AssistGestureSettings.java create mode 100644 tests/robotests/src/com/android/settings/gestures/AssistGesturePreferenceControllerTest.java create mode 100644 tests/robotests/src/com/android/settings/gestures/AssistGestureSettingsTest.java diff --git a/res/drawable-nodpi/gesture_assist b/res/drawable-nodpi/gesture_assist new file mode 100644 index 0000000000..e69de29bb2 diff --git a/res/raw/gesture_assist.mp4 b/res/raw/gesture_assist.mp4 new file mode 100644 index 0000000000..e69de29bb2 diff --git a/res/values/strings.xml b/res/values/strings.xml index fcb57ad2d8..381d175e67 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -8030,6 +8030,12 @@ To check your notifications, swipe down on the fingerprint sensor on the back of your device. + + Assist gesture + + + + On Off diff --git a/res/xml/assist_gesture_settings.xml b/res/xml/assist_gesture_settings.xml new file mode 100644 index 0000000000..c0a3810970 --- /dev/null +++ b/res/xml/assist_gesture_settings.xml @@ -0,0 +1,32 @@ + + + + + + + + + + diff --git a/res/xml/gesture_settings.xml b/res/xml/gesture_settings.xml index 89549166c7..808c9b2083 100644 --- a/res/xml/gesture_settings.xml +++ b/res/xml/gesture_settings.xml @@ -20,6 +20,13 @@ settings:keywords="@string/keywords_gesture"> + + - \ No newline at end of file + diff --git a/res/xml/language_and_input.xml b/res/xml/language_and_input.xml index 5beb71ca5d..6303fbd383 100644 --- a/res/xml/language_and_input.xml +++ b/res/xml/language_and_input.xml @@ -53,6 +53,11 @@ android:title="@string/gesture_preference_title"> + + @@ -114,4 +119,4 @@ - \ No newline at end of file + diff --git a/res/xml/manage_assist.xml b/res/xml/manage_assist.xml index 993bf91641..0ba87b5c6c 100644 --- a/res/xml/manage_assist.xml +++ b/res/xml/manage_assist.xml @@ -24,6 +24,11 @@ android:title="@string/default_assist_title" android:summary="@string/default_assist_none"/> + + getPreferenceControllers(Context context) { + final List controllers = new ArrayList<>(); + controllers.add(new AssistGesturePreferenceController(context, getLifecycle())); + return controllers; + } + + public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = + new BaseSearchIndexProvider() { + @Override + public List getXmlResourcesToIndex( + Context context, boolean enabled) { + if (!FeatureFactory.getFactory(context).getDashboardFeatureProvider(context) + .isEnabled()) { + return null; + } + final SearchIndexableResource sir = new SearchIndexableResource(context); + sir.xmlResId = R.xml.assist_gesture_settings; + return Arrays.asList(sir); + } + }; +} + diff --git a/src/com/android/settings/gestures/GestureSettings.java b/src/com/android/settings/gestures/GestureSettings.java index db6925e834..821f2205c7 100644 --- a/src/com/android/settings/gestures/GestureSettings.java +++ b/src/com/android/settings/gestures/GestureSettings.java @@ -58,6 +58,7 @@ public class GestureSettings extends DashboardFragment { final AmbientDisplayConfiguration ambientConfig = new AmbientDisplayConfiguration(context); final List controllers = new ArrayList<>(); final Lifecycle lifecycle = getLifecycle(); + controllers.add(new AssistGesturePreferenceController(context, lifecycle)); controllers.add(new SwipeToNotificationPreferenceController(context, lifecycle)); controllers.add(new DoubleTapPowerPreferenceController(context, lifecycle)); controllers.add(new DoubleTwistPreferenceController(context, lifecycle)); @@ -72,6 +73,7 @@ public class GestureSettings extends DashboardFragment { public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { super.onCreatePreferences(savedInstanceState, rootKey); mPreferences = new ArrayList(); + addPreferenceToTrackingList(AssistGesturePreferenceController.class); addPreferenceToTrackingList(SwipeToNotificationPreferenceController.class); addPreferenceToTrackingList(DoubleTapScreenPreferenceController.class); addPreferenceToTrackingList(DoubleTwistPreferenceController.class); diff --git a/src/com/android/settings/language/LanguageAndInputSettings.java b/src/com/android/settings/language/LanguageAndInputSettings.java index 4f82eb87b1..39872522af 100644 --- a/src/com/android/settings/language/LanguageAndInputSettings.java +++ b/src/com/android/settings/language/LanguageAndInputSettings.java @@ -28,6 +28,7 @@ import com.android.settings.R; import com.android.settings.core.PreferenceController; import com.android.settings.core.lifecycle.Lifecycle; import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.gestures.AssistGesturePreferenceController; import com.android.settings.gestures.DoubleTapPowerPreferenceController; import com.android.settings.gestures.DoubleTapScreenPreferenceController; import com.android.settings.gestures.DoubleTwistPreferenceController; @@ -88,7 +89,7 @@ public class LanguageAndInputSettings extends DashboardFragment { } controllers.add(gameControllerPreferenceController); // Gestures - + controllers.add(new AssistGesturePreferenceController(context, lifecycle)); controllers.add(new SwipeToNotificationPreferenceController(context, lifecycle)); controllers.add(new DoubleTwistPreferenceController(context, lifecycle)); controllers.add(new DoubleTapPowerPreferenceController(context, lifecycle)); diff --git a/src/com/android/settings/overlay/FeatureFactory.java b/src/com/android/settings/overlay/FeatureFactory.java index 5dd202ed7d..be55598d97 100644 --- a/src/com/android/settings/overlay/FeatureFactory.java +++ b/src/com/android/settings/overlay/FeatureFactory.java @@ -27,6 +27,7 @@ import com.android.settings.dashboard.DashboardFeatureProvider; import com.android.settings.dashboard.SuggestionFeatureProvider; import com.android.settings.enterprise.EnterprisePrivacyFeatureProvider; import com.android.settings.fuelgauge.PowerUsageFeatureProvider; +import com.android.settings.gestures.AssistGestureFeatureProvider; import com.android.settings.localepicker.LocaleFeatureProvider; import com.android.settings.security.SecurityFeatureProvider; import com.android.settings.search2.SearchFeatureProvider; @@ -68,6 +69,8 @@ public abstract class FeatureFactory { return sFactory; } + public abstract AssistGestureFeatureProvider getAssistGestureFeatureProvider(); + public abstract SuggestionFeatureProvider getSuggestionFeatureProvider(); public abstract SupportFeatureProvider getSupportFeatureProvider(Context context); diff --git a/src/com/android/settings/overlay/FeatureFactoryImpl.java b/src/com/android/settings/overlay/FeatureFactoryImpl.java index 4c81e309ed..7a9e065ce4 100644 --- a/src/com/android/settings/overlay/FeatureFactoryImpl.java +++ b/src/com/android/settings/overlay/FeatureFactoryImpl.java @@ -38,6 +38,8 @@ import com.android.settings.enterprise.EnterprisePrivacyFeatureProvider; import com.android.settings.enterprise.EnterprisePrivacyFeatureProviderImpl; import com.android.settings.fuelgauge.PowerUsageFeatureProvider; import com.android.settings.fuelgauge.PowerUsageFeatureProviderImpl; +import com.android.settings.gestures.AssistGestureFeatureProvider; +import com.android.settings.gestures.AssistGestureFeatureProviderImpl; import com.android.settings.localepicker.LocaleFeatureProvider; import com.android.settings.localepicker.LocaleFeatureProviderImpl; import com.android.settings.search2.SearchFeatureProvider; @@ -61,6 +63,7 @@ public class FeatureFactoryImpl extends FeatureFactory { private SecurityFeatureProvider mSecurityFeatureProvider; private SuggestionFeatureProvider mSuggestionFeatureProvider; private PowerUsageFeatureProvider mPowerUsageFeatureProvider; + private AssistGestureFeatureProvider mAssistGestureFeatureProvider; @Override public SupportFeatureProvider getSupportFeatureProvider(Context context) { @@ -154,4 +157,12 @@ public class FeatureFactoryImpl extends FeatureFactory { } return mSuggestionFeatureProvider; } + + @Override + public AssistGestureFeatureProvider getAssistGestureFeatureProvider() { + if (mAssistGestureFeatureProvider == null) { + mAssistGestureFeatureProvider = new AssistGestureFeatureProviderImpl(); + } + return mAssistGestureFeatureProvider; + } } diff --git a/src/com/android/settings/search/SearchIndexableResources.java b/src/com/android/settings/search/SearchIndexableResources.java index ada8200985..700ab72a5c 100644 --- a/src/com/android/settings/search/SearchIndexableResources.java +++ b/src/com/android/settings/search/SearchIndexableResources.java @@ -50,6 +50,7 @@ import com.android.settings.fuelgauge.BatterySaverSettings; import com.android.settings.fuelgauge.PowerUsageAdvanced; import com.android.settings.fuelgauge.PowerUsageDetail; import com.android.settings.fuelgauge.PowerUsageSummary; +import com.android.settings.gestures.AssistGestureSettings; import com.android.settings.gestures.DoubleTapPowerSettings; import com.android.settings.gestures.DoubleTapScreenSettings; import com.android.settings.gestures.DoubleTwistGestureSettings; @@ -136,6 +137,7 @@ public final class SearchIndexableResources { R.xml.special_access, R.drawable.ic_settings_applications); addIndex(UserSettings.class, NO_DATA_RES_ID, R.drawable.ic_settings_multiuser); addIndex(GestureSettings.class, NO_DATA_RES_ID, R.drawable.ic_settings_gestures); + addIndex(AssistGestureSettings.class, NO_DATA_RES_ID, R.drawable.ic_settings_gestures); addIndex(PickupGestureSettings.class, NO_DATA_RES_ID, R.drawable.ic_settings_gestures); addIndex(DoubleTapScreenSettings.class, NO_DATA_RES_ID, R.drawable.ic_settings_gestures); addIndex(DoubleTapPowerSettings.class, NO_DATA_RES_ID, R.drawable.ic_settings_gestures); diff --git a/tests/robotests/src/com/android/settings/gestures/AssistGesturePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/gestures/AssistGesturePreferenceControllerTest.java new file mode 100644 index 0000000000..9862f83bdf --- /dev/null +++ b/tests/robotests/src/com/android/settings/gestures/AssistGesturePreferenceControllerTest.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.gestures; + +import android.content.Context; +import android.provider.Settings; + +import com.android.settings.SettingsRobolectricTestRunner; +import com.android.settings.TestConfig; +import com.android.settings.testutils.FakeFeatureFactory; + +import com.android.settings.search2.InlineSwitchPayload; +import com.android.settings.search2.ResultPayload; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Answers; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.annotation.Config; +import org.robolectric.shadows.ShadowApplication; + +import java.util.ArrayList; +import java.util.List; + +import static android.provider.Settings.Secure.ASSIST_GESTURE_ENABLED; +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class AssistGesturePreferenceControllerTest { + + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private Context mContext; + private FakeFeatureFactory mFactory; + private AssistGesturePreferenceController mController; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + FakeFeatureFactory.setupForTest(mContext); + mFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext); + mController = new AssistGesturePreferenceController(mContext, null); + } + + @Test + public void isAvailable_whenSupported_shouldReturnTrue() { + when(mFactory.assistGestureFeatureProvider.isSupported(mContext)).thenReturn(true); + assertThat(mController.isAvailable()).isTrue(); + } + + @Test + public void isAvailable_whenUnsupported_shouldReturnFalse() { + when(mFactory.assistGestureFeatureProvider.isSupported(mContext)).thenReturn(false); + assertThat(mController.isAvailable()).isFalse(); + } + + @Test + public void testSwitchEnabled_configIsSet_shouldReturnTrue() { + // Set the setting to be enabled. + final Context context = ShadowApplication.getInstance().getApplicationContext(); + Settings.System.putInt(context.getContentResolver(), ASSIST_GESTURE_ENABLED, 1); + mController = new AssistGesturePreferenceController(context, null); + + assertThat(mController.isSwitchPrefEnabled()).isTrue(); + } + + @Test + public void testSwitchEnabled_configIsNotSet_shouldReturnFalse() { + // Set the setting to be disabled. + final Context context = ShadowApplication.getInstance().getApplicationContext(); + Settings.System.putInt(context.getContentResolver(), ASSIST_GESTURE_ENABLED, 0); + mController = new AssistGesturePreferenceController(context, null); + + assertThat(mController.isSwitchPrefEnabled()).isFalse(); + } +} + diff --git a/tests/robotests/src/com/android/settings/gestures/AssistGestureSettingsTest.java b/tests/robotests/src/com/android/settings/gestures/AssistGestureSettingsTest.java new file mode 100644 index 0000000000..285a393335 --- /dev/null +++ b/tests/robotests/src/com/android/settings/gestures/AssistGestureSettingsTest.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.gestures; + +import android.content.Context; +import android.provider.SearchIndexableResource; + +import com.android.settings.R; +import com.android.settings.SettingsRobolectricTestRunner; +import com.android.settings.TestConfig; +import com.android.settings.core.PreferenceController; +import com.android.settings.testutils.FakeFeatureFactory; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Answers; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.annotation.Config; +import org.robolectric.shadows.ShadowApplication; + +import java.util.List; + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.when; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class AssistGestureSettingsTest { + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private Context mContext; + private AssistGestureSettings mSettings; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + FakeFeatureFactory.setupForTest(mContext); + final FakeFeatureFactory factory = + (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext); + when(factory.dashboardFeatureProvider.isEnabled()).thenReturn(true); + mSettings = new AssistGestureSettings(); + } + + @Test + public void testGetPreferenceScreenResId() { + assertThat(mSettings.getPreferenceScreenResId()) + .isEqualTo(R.xml.assist_gesture_settings); + } + + @Test + public void testGetPreferenceControllers_shouldAllBeCreated() { + final List controllers = + mSettings.getPreferenceControllers(mContext); + assertThat(controllers.isEmpty()).isFalse(); + } + + @Test + public void testSearchIndexProvider_shouldIndexResource() { + final List indexRes = + AssistGestureSettings.SEARCH_INDEX_DATA_PROVIDER.getXmlResourcesToIndex( + ShadowApplication.getInstance().getApplicationContext(), + true /* enabled */); + + assertThat(indexRes).isNotNull(); + assertThat(indexRes.get(0).xmlResId).isEqualTo(mSettings.getPreferenceScreenResId()); + } +} + diff --git a/tests/robotests/src/com/android/settings/search/DatabaseIndexingManagerTest.java b/tests/robotests/src/com/android/settings/search/DatabaseIndexingManagerTest.java index 2e13393748..c3f72b378c 100644 --- a/tests/robotests/src/com/android/settings/search/DatabaseIndexingManagerTest.java +++ b/tests/robotests/src/com/android/settings/search/DatabaseIndexingManagerTest.java @@ -210,7 +210,7 @@ public class DatabaseIndexingManagerTest { mManager.indexOneSearchIndexableData(mDb, localeStr, resource, new HashMap<>()); Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null); - assertThat(cursor.getCount()).isEqualTo(6); + assertThat(cursor.getCount()).isEqualTo(7); } @Test @@ -224,7 +224,7 @@ public class DatabaseIndexingManagerTest { Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index WHERE enabled = 0", null); assertThat(cursor.getCount()).isEqualTo(2); cursor = mDb.rawQuery("SELECT * FROM prefs_index WHERE enabled = 1", null); - assertThat(cursor.getCount()).isEqualTo(4); + assertThat(cursor.getCount()).isEqualTo(5); } @Test @@ -673,4 +673,4 @@ public class DatabaseIndexingManagerTest { niks.put(packageName, keysList); return niks; } -} \ No newline at end of file +} diff --git a/tests/robotests/src/com/android/settings/search/XmlParserUtilTest.java b/tests/robotests/src/com/android/settings/search/XmlParserUtilTest.java index d0c691c51f..300aaaf71c 100644 --- a/tests/robotests/src/com/android/settings/search/XmlParserUtilTest.java +++ b/tests/robotests/src/com/android/settings/search/XmlParserUtilTest.java @@ -60,7 +60,7 @@ public class XmlParserUtilTest { "com.android.settings.gestures.GesturePreference"); final AttributeSet attrs = Xml.asAttributeSet(parser); String title = XmlParserUtils.getDataTitle(mContext, attrs); - String expTitle = mContext.getString(R.string.fingerprint_swipe_for_notifications_title); + String expTitle = mContext.getString(R.string.assist_gesture_title); assertThat(title).isEqualTo(expTitle); } @@ -79,7 +79,7 @@ public class XmlParserUtilTest { "com.android.settings.gestures.GesturePreference"); final AttributeSet attrs = Xml.asAttributeSet(parser); String key = XmlParserUtils.getDataKey(mContext, attrs); - String expKey = "gesture_swipe_down_fingerprint"; + String expKey = "gesture_assist"; assertThat(key).isEqualTo(expKey); } @@ -89,7 +89,7 @@ public class XmlParserUtilTest { "com.android.settings.gestures.GesturePreference"); final AttributeSet attrs = Xml.asAttributeSet(parser); String summary = XmlParserUtils.getDataSummary(mContext, attrs); - String expSummary = mContext.getString(R.string.fingerprint_swipe_for_notifications_summary); + String expSummary = mContext.getString(R.string.assist_gesture_summary); assertThat(summary).isEqualTo(expSummary); } diff --git a/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java b/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java index 0352e0f55b..b5a2fe06f2 100644 --- a/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java +++ b/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java @@ -23,6 +23,7 @@ import com.android.settings.dashboard.DashboardFeatureProvider; import com.android.settings.dashboard.SuggestionFeatureProvider; import com.android.settings.enterprise.EnterprisePrivacyFeatureProvider; import com.android.settings.fuelgauge.PowerUsageFeatureProvider; +import com.android.settings.gestures.AssistGestureFeatureProvider; import com.android.settings.localepicker.LocaleFeatureProvider; import com.android.settings.overlay.FeatureFactory; import com.android.settings.overlay.SupportFeatureProvider; @@ -51,6 +52,7 @@ public class FakeFeatureFactory extends FeatureFactory { public final SurveyFeatureProvider surveyFeatureProvider; public final SecurityFeatureProvider securityFeatureProvider; public final SuggestionFeatureProvider suggestionsFeatureProvider; + public final AssistGestureFeatureProvider assistGestureFeatureProvider; /** * Call this in {@code @Before} method of the test class to use fake factory. @@ -84,6 +86,7 @@ public class FakeFeatureFactory extends FeatureFactory { surveyFeatureProvider = mock(SurveyFeatureProvider.class); securityFeatureProvider = mock(SecurityFeatureProvider.class); suggestionsFeatureProvider = mock(SuggestionFeatureProvider.class); + assistGestureFeatureProvider = mock(AssistGestureFeatureProvider.class); } @Override @@ -140,4 +143,9 @@ public class FakeFeatureFactory extends FeatureFactory { public SecurityFeatureProvider getSecurityFeatureProvider() { return securityFeatureProvider; } + + @Override + public AssistGestureFeatureProvider getAssistGestureFeatureProvider() { + return assistGestureFeatureProvider; + } } -- 2.11.0