From 03fb1a93ca7b6beb7766b130d294ad0f7dd74ea6 Mon Sep 17 00:00:00 2001 From: Doris Ling Date: Fri, 13 Jul 2018 12:32:00 -0700 Subject: [PATCH 1/8] DO NOT MERGE Fix summary text for advanced button. - the resource for the summary text is in the support library. However, the prebuilt version in the build does not contain the latest change for the summary text. Add the overlay for the summary resource here with the updated translation to remove the redundant summary prefix in the advanced button. Change-Id: I0fa8e3c9e02090d7d8a3e18608181ab73d2f46a1 Fixes: 118315852 Test: manual --- res/values-zh-rCN/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml index c6cab6ee10e..686aed7b363 100644 --- a/res/values-zh-rCN/strings.xml +++ b/res/values-zh-rCN/strings.xml @@ -16,6 +16,7 @@ + "%1$s%2$s" "是" "否" "创建" From 95f8c6e96a8be9dfb0ae933abb4263455a941b80 Mon Sep 17 00:00:00 2001 From: clownshen Date: Wed, 31 Oct 2018 13:28:09 +0800 Subject: [PATCH 2/8] Launch WfcActivation with subscription ID For multi SIM project, need to have subscription ID information in the intent for launching WFC activation activity. Bug: 112826472 Test: manual test Change-Id: I9f470c014054efa612c87ff16e574fc9fd286b3d --- .../android/settings/wifi/calling/WifiCallingSettingsForSub.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java b/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java index 15137cc821a..00f4758fe34 100644 --- a/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java +++ b/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java @@ -398,6 +398,7 @@ public class WifiCallingSettingsForSub extends SettingsPreferenceFragment // Build and return intent Intent intent = new Intent(); intent.setComponent(componentName); + intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, mSubId); return intent; } From c1fd6dc5a391e2c99054fe49c15077aa6a2a222b Mon Sep 17 00:00:00 2001 From: Beverly Date: Thu, 1 Nov 2018 18:08:26 -0400 Subject: [PATCH 3/8] Use inline radio buttons for Prevent Ringing Test: atest PreventRingingGesturePreferenceControllerTest Fixes: 79867902 Change-Id: I3c9f949e038bba98e5be972adc01f35f5513432a --- res/values/strings.xml | 2 +- res/xml/prevent_ringing_gesture_settings.xml | 13 +- .../GesturesSettingPreferenceController.java | 6 +- ...entRingingGesturePreferenceController.java | 222 ++++++++++++++++++ .../PreventRingingGestureSettings.java | 21 ++ .../PreventRingingPreferenceController.java | 132 ----------- ...ingingGesturePreferenceControllerTest.java | 152 ++++++++++++ ...reventRingingPreferenceControllerTest.java | 122 ---------- 8 files changed, 403 insertions(+), 267 deletions(-) create mode 100644 src/com/android/settings/gestures/PreventRingingGesturePreferenceController.java delete mode 100644 src/com/android/settings/gestures/PreventRingingPreferenceController.java create mode 100644 tests/robotests/src/com/android/settings/gestures/PreventRingingGesturePreferenceControllerTest.java delete mode 100644 tests/robotests/src/com/android/settings/gestures/PreventRingingPreferenceControllerTest.java diff --git a/res/values/strings.xml b/res/values/strings.xml index 4a2443a2401..462f7ff6255 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -10064,7 +10064,7 @@ Prevent ringing - Press Power & Volume Up together + Press Power & Volume Up together to Shortcut to prevent ringing diff --git a/res/xml/prevent_ringing_gesture_settings.xml b/res/xml/prevent_ringing_gesture_settings.xml index 5135664d6f6..62f022301a4 100644 --- a/res/xml/prevent_ringing_gesture_settings.xml +++ b/res/xml/prevent_ringing_gesture_settings.xml @@ -26,13 +26,8 @@ app:animation="@raw/gesture_prevent_ringing" app:preview="@drawable/gesture_prevent_ringing" /> - - + + \ No newline at end of file diff --git a/src/com/android/settings/gestures/GesturesSettingPreferenceController.java b/src/com/android/settings/gestures/GesturesSettingPreferenceController.java index 069085bd451..2e9b5a5c504 100644 --- a/src/com/android/settings/gestures/GesturesSettingPreferenceController.java +++ b/src/com/android/settings/gestures/GesturesSettingPreferenceController.java @@ -20,8 +20,6 @@ import android.content.ContentResolver; import android.content.Context; import android.provider.Settings; -import androidx.annotation.NonNull; - import com.android.internal.hardware.AmbientDisplayConfiguration; import com.android.settings.R; import com.android.settings.core.BasePreferenceController; @@ -31,6 +29,8 @@ import com.android.settingslib.core.AbstractPreferenceController; import java.util.ArrayList; import java.util.List; +import androidx.annotation.NonNull; + public class GesturesSettingPreferenceController extends BasePreferenceController { private final AssistGestureFeatureProvider mFeatureProvider; private List mGestureControllers; @@ -76,7 +76,7 @@ public class GesturesSettingPreferenceController extends BasePreferenceControlle .setConfig(ambientDisplayConfiguration)); controllers.add(new DoubleTapScreenPreferenceController(context, FAKE_PREF_KEY) .setConfig(ambientDisplayConfiguration)); - controllers.add(new PreventRingingPreferenceController(context, FAKE_PREF_KEY)); + controllers.add(new PreventRingingParentPreferenceController(context, FAKE_PREF_KEY)); return controllers; } diff --git a/src/com/android/settings/gestures/PreventRingingGesturePreferenceController.java b/src/com/android/settings/gestures/PreventRingingGesturePreferenceController.java new file mode 100644 index 00000000000..cb9bf4f5881 --- /dev/null +++ b/src/com/android/settings/gestures/PreventRingingGesturePreferenceController.java @@ -0,0 +1,222 @@ +/* + * Copyright (C) 2018 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.ContentResolver; +import android.content.Context; +import android.database.ContentObserver; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.provider.Settings; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.settings.R; +import com.android.settings.core.PreferenceControllerMixin; +import com.android.settings.widget.RadioButtonPreference; +import com.android.settings.widget.VideoPreference; +import com.android.settingslib.core.AbstractPreferenceController; +import com.android.settingslib.core.lifecycle.Lifecycle; +import com.android.settingslib.core.lifecycle.LifecycleObserver; +import com.android.settingslib.core.lifecycle.events.OnCreate; +import com.android.settingslib.core.lifecycle.events.OnPause; +import com.android.settingslib.core.lifecycle.events.OnResume; +import com.android.settingslib.core.lifecycle.events.OnSaveInstanceState; + +import androidx.preference.Preference; +import androidx.preference.PreferenceCategory; +import androidx.preference.PreferenceScreen; + +public class PreventRingingGesturePreferenceController extends AbstractPreferenceController + implements RadioButtonPreference.OnClickListener, LifecycleObserver, OnSaveInstanceState, + OnResume, OnPause, OnCreate, PreferenceControllerMixin { + + @VisibleForTesting static final String KEY_VIBRATE = "prevent_ringing_option_vibrate"; + @VisibleForTesting static final String KEY_NONE = "prevent_ringing_option_none"; + @VisibleForTesting static final String KEY_MUTE = "prevent_ringing_option_mute"; + + private final String KEY_VIDEO_PAUSED = "key_video_paused"; + private final String PREF_KEY_VIDEO = "gesture_prevent_ringing_video"; + private final String KEY = "gesture_prevent_ringing_category"; + private final Context mContext; + + private VideoPreference mVideoPreference; + private boolean mVideoPaused; + + private PreferenceCategory mPreferenceCategory; + @VisibleForTesting RadioButtonPreference mVibratePref; + @VisibleForTesting RadioButtonPreference mNonePref; + @VisibleForTesting RadioButtonPreference mMutePref; + + private SettingObserver mSettingObserver; + + public PreventRingingGesturePreferenceController(Context context, Lifecycle lifecycle) { + super(context); + mContext = context; + + if (lifecycle != null) { + lifecycle.addObserver(this); + } + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + if (isAvailable()) { + mPreferenceCategory = (PreferenceCategory) screen.findPreference(getPreferenceKey()); + mVibratePref = makeRadioPreference(KEY_VIBRATE, R.string.prevent_ringing_option_vibrate); + mMutePref = makeRadioPreference(KEY_MUTE, R.string.prevent_ringing_option_mute); + mNonePref = makeRadioPreference(KEY_NONE, R.string.prevent_ringing_option_none); + + if (mPreferenceCategory != null) { + mSettingObserver = new SettingObserver(mPreferenceCategory); + } + + mVideoPreference = (VideoPreference) screen.findPreference(getVideoPrefKey()); + } + } + + @Override + public boolean isAvailable() { + return mContext.getResources().getBoolean( + com.android.internal.R.bool.config_volumeHushGestureEnabled); + } + + @Override + public String getPreferenceKey() { + return KEY; + } + + public String getVideoPrefKey() { + return PREF_KEY_VIDEO; + } + + @Override + public void onSaveInstanceState(Bundle outState) { + outState.putBoolean(KEY_VIDEO_PAUSED, mVideoPaused); + } + + @Override + public void onRadioButtonClicked(RadioButtonPreference preference) { + int preventRingingSetting = keyToSetting(preference.getKey()); + if (preventRingingSetting != Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.VOLUME_HUSH_GESTURE, Settings.Secure.VOLUME_HUSH_VIBRATE)) { + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.VOLUME_HUSH_GESTURE, preventRingingSetting); + } + } + + @Override + public void updateState(Preference preference) { + int preventRingingSetting = Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.VOLUME_HUSH_GESTURE, Settings.Secure.VOLUME_HUSH_VIBRATE); + + final boolean isVibrate = preventRingingSetting == Settings.Secure.VOLUME_HUSH_VIBRATE; + final boolean isMute = preventRingingSetting == Settings.Secure.VOLUME_HUSH_MUTE; + final boolean isOff = preventRingingSetting == Settings.Secure.VOLUME_HUSH_OFF + || (!isVibrate && !isMute); + if (mVibratePref != null && mVibratePref.isChecked() != isVibrate) { + mVibratePref.setChecked(isVibrate); + } + if (mMutePref != null && mMutePref.isChecked() != isMute) { + mMutePref.setChecked(isMute); + } + if (mNonePref != null && mNonePref.isChecked() != isOff) { + mNonePref.setChecked(isOff); + } + } + + @Override + public void onCreate(Bundle savedInstanceState) { + if (savedInstanceState != null) { + mVideoPaused = savedInstanceState.getBoolean(KEY_VIDEO_PAUSED, false); + } + } + + @Override + public void onResume() { + if (mSettingObserver != null) { + mSettingObserver.register(mContext.getContentResolver()); + mSettingObserver.onChange(false, null); + } + + if (mVideoPreference != null) { + mVideoPreference.onViewVisible(mVideoPaused); + } + } + + @Override + public void onPause() { + if (mSettingObserver != null) { + mSettingObserver.unregister(mContext.getContentResolver()); + } + + if (mVideoPreference != null) { + mVideoPaused = mVideoPreference.isVideoPaused(); + mVideoPreference.onViewInvisible(); + } + } + + private int keyToSetting(String key) { + switch (key) { + case KEY_NONE: + return Settings.Secure.VOLUME_HUSH_OFF; + case KEY_MUTE: + return Settings.Secure.VOLUME_HUSH_MUTE; + case KEY_VIBRATE: + default: + return Settings.Secure.VOLUME_HUSH_VIBRATE; + } + } + + private RadioButtonPreference makeRadioPreference(String key, int titleId) { + RadioButtonPreference pref = new RadioButtonPreference(mPreferenceCategory.getContext()); + pref.setKey(key); + pref.setTitle(titleId); + pref.setOnClickListener(this); + mPreferenceCategory.addPreference(pref); + return pref; + } + + private class SettingObserver extends ContentObserver { + private final Uri VOLUME_HUSH_GESTURE = Settings.Secure.getUriFor( + Settings.Secure.VOLUME_HUSH_GESTURE); + + private final Preference mPreference; + + public SettingObserver(Preference preference) { + super(new Handler()); + mPreference = preference; + } + + public void register(ContentResolver cr) { + cr.registerContentObserver(VOLUME_HUSH_GESTURE, false, this); + } + + public void unregister(ContentResolver cr) { + cr.unregisterContentObserver(this); + } + + @Override + public void onChange(boolean selfChange, Uri uri) { + super.onChange(selfChange, uri); + if (uri == null || VOLUME_HUSH_GESTURE.equals(uri)) { + updateState(mPreference); + } + } + } +} diff --git a/src/com/android/settings/gestures/PreventRingingGestureSettings.java b/src/com/android/settings/gestures/PreventRingingGestureSettings.java index 84b4e164169..3e8ae855227 100644 --- a/src/com/android/settings/gestures/PreventRingingGestureSettings.java +++ b/src/com/android/settings/gestures/PreventRingingGestureSettings.java @@ -23,8 +23,11 @@ import com.android.internal.logging.nano.MetricsProto; import com.android.settings.R; import com.android.settings.dashboard.DashboardFragment; import com.android.settings.search.BaseSearchIndexProvider; +import com.android.settingslib.core.AbstractPreferenceController; +import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.search.SearchIndexable; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -39,6 +42,18 @@ public class PreventRingingGestureSettings extends DashboardFragment { super.onAttach(context); } + @Override + protected List createPreferenceControllers(Context context) { + return buildPreferenceControllers(context, getSettingsLifecycle()); + } + + private static List buildPreferenceControllers(Context context, + Lifecycle lifecycle) { + List controllers = new ArrayList<>(); + controllers.add(new PreventRingingGesturePreferenceController(context, lifecycle)); + return controllers; + } + @Override public int getMetricsCategory() { return MetricsProto.MetricsEvent.SETTINGS_PREVENT_RINGING; @@ -68,6 +83,12 @@ public class PreventRingingGestureSettings extends DashboardFragment { sir.xmlResId = R.xml.prevent_ringing_gesture_settings; return Arrays.asList(sir); } + + @Override + public List createPreferenceControllers( + Context context) { + return buildPreferenceControllers(context, null); + } }; } diff --git a/src/com/android/settings/gestures/PreventRingingPreferenceController.java b/src/com/android/settings/gestures/PreventRingingPreferenceController.java deleted file mode 100644 index 570c1c61d75..00000000000 --- a/src/com/android/settings/gestures/PreventRingingPreferenceController.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (C) 2018 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 static android.provider.Settings.Secure.VOLUME_HUSH_MUTE; -import static android.provider.Settings.Secure.VOLUME_HUSH_OFF; -import static android.provider.Settings.Secure.VOLUME_HUSH_VIBRATE; - -import android.content.Context; -import android.os.Bundle; -import android.provider.Settings; - -import androidx.annotation.VisibleForTesting; -import androidx.preference.ListPreference; -import androidx.preference.Preference; -import androidx.preference.PreferenceScreen; - -import com.android.settings.widget.VideoPreference; -import com.android.settingslib.core.lifecycle.LifecycleObserver; -import com.android.settingslib.core.lifecycle.events.OnCreate; -import com.android.settingslib.core.lifecycle.events.OnPause; -import com.android.settingslib.core.lifecycle.events.OnResume; -import com.android.settingslib.core.lifecycle.events.OnSaveInstanceState; - -public class PreventRingingPreferenceController extends PreventRingingParentPreferenceController - implements Preference.OnPreferenceChangeListener, - LifecycleObserver, OnResume, OnPause, OnCreate, OnSaveInstanceState { - - private static final String PREF_KEY_VIDEO = "gesture_prevent_ringing_video"; - @VisibleForTesting - static final String KEY_VIDEO_PAUSED = "key_video_paused"; - - private VideoPreference mVideoPreference; - @VisibleForTesting - boolean mVideoPaused; - - public PreventRingingPreferenceController(Context context, String key) { - super(context, key); - } - - @Override - public int getAvailabilityStatus() { - final int status = super.getAvailabilityStatus(); - if (status == AVAILABLE_UNSEARCHABLE) { - return AVAILABLE; - } - return status; - } - - @Override - public void displayPreference(PreferenceScreen screen) { - super.displayPreference(screen); - if (isAvailable()) { - mVideoPreference = (VideoPreference) screen.findPreference(getVideoPrefKey()); - } - } - - @Override - public void updateState(Preference preference) { - super.updateState(preference); - if (preference != null) { - if (preference instanceof ListPreference) { - ListPreference pref = (ListPreference) preference; - int value = Settings.Secure.getInt( - mContext.getContentResolver(), SECURE_KEY, VOLUME_HUSH_VIBRATE); - switch (value) { - case VOLUME_HUSH_VIBRATE: - pref.setValue(String.valueOf(value)); - break; - case VOLUME_HUSH_MUTE: - pref.setValue(String.valueOf(value)); - break; - default: - pref.setValue(String.valueOf(VOLUME_HUSH_OFF)); - } - } - } - } - - @Override - public void onCreate(Bundle savedInstanceState) { - if (savedInstanceState != null) { - mVideoPaused = savedInstanceState.getBoolean(KEY_VIDEO_PAUSED, false); - } - } - - @Override - public void onSaveInstanceState(Bundle outState) { - outState.putBoolean(KEY_VIDEO_PAUSED, mVideoPaused); - } - - @Override - public void onPause() { - if (mVideoPreference != null) { - mVideoPaused = mVideoPreference.isVideoPaused(); - mVideoPreference.onViewInvisible(); - } - } - - @Override - public void onResume() { - if (mVideoPreference != null) { - mVideoPreference.onViewVisible(mVideoPaused); - } - } - - protected String getVideoPrefKey() { - return PREF_KEY_VIDEO; - } - - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - int value = Integer.parseInt((String) newValue); - Settings.Secure.putInt(mContext.getContentResolver(), SECURE_KEY, value); - preference.setSummary(getSummary()); - return true; - } -} diff --git a/tests/robotests/src/com/android/settings/gestures/PreventRingingGesturePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/gestures/PreventRingingGesturePreferenceControllerTest.java new file mode 100644 index 00000000000..6d77d772c71 --- /dev/null +++ b/tests/robotests/src/com/android/settings/gestures/PreventRingingGesturePreferenceControllerTest.java @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2018 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 static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; + +import android.content.Context; +import android.content.res.Resources; +import android.preference.PreferenceCategory; +import android.provider.Settings; + +import androidx.preference.PreferenceScreen; + +import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settings.widget.RadioButtonPreference; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Answers; +import org.mockito.Mock; +import org.robolectric.RuntimeEnvironment; + +@RunWith(SettingsRobolectricTestRunner.class) +public class PreventRingingGesturePreferenceControllerTest { + + private Context mContext; + private Resources mResources; + private PreventRingingGesturePreferenceController mController; + + @Before + public void setUp() { + mContext = spy(RuntimeEnvironment.application); + mResources = mock(Resources.class); + when(mContext.getResources()).thenReturn(mResources); + when(mResources.getBoolean(com.android.internal.R.bool.config_volumeHushGestureEnabled)) + .thenReturn(true); + mController = new PreventRingingGesturePreferenceController(mContext, null); + mController.mVibratePref = new RadioButtonPreference(mContext); + mController.mNonePref = new RadioButtonPreference(mContext); + mController.mMutePref = new RadioButtonPreference(mContext); + } + + @Test + public void testIsAvailable_configIsTrue_shouldReturnTrue() { + when(mResources.getBoolean( + com.android.internal.R.bool.config_volumeHushGestureEnabled)).thenReturn(true); + + assertThat(mController.isAvailable()).isTrue(); + } + + @Test + public void testIsAvailable_configIsFalse_shouldReturnFalse() { + when(mResources.getBoolean( + com.android.internal.R.bool.config_volumeHushGestureEnabled)).thenReturn(false); + + assertThat(mController.isAvailable()).isFalse(); + } + + @Test + public void testUpdateState_mute() { + Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.VOLUME_HUSH_GESTURE, + Settings.Secure.VOLUME_HUSH_MUTE); + mController.updateState(null); + assertThat(mController.mVibratePref.isChecked()).isFalse(); + assertThat(mController.mNonePref.isChecked()).isFalse(); + assertThat(mController.mMutePref.isChecked()).isTrue(); + } + + @Test + public void testUpdateState_vibrate() { + Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.VOLUME_HUSH_GESTURE, + Settings.Secure.VOLUME_HUSH_VIBRATE); + mController.updateState(null); + assertThat(mController.mVibratePref.isChecked()).isTrue(); + assertThat(mController.mNonePref.isChecked()).isFalse(); + assertThat(mController.mMutePref.isChecked()).isFalse(); + } + + @Test + public void testUpdateState_other() { + Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.VOLUME_HUSH_GESTURE, + 7); + mController.updateState(null); + assertThat(mController.mVibratePref.isChecked()).isFalse(); + assertThat(mController.mNonePref.isChecked()).isTrue(); + assertThat(mController.mMutePref.isChecked()).isFalse(); + } + + @Test + public void testRadioButtonClicked_mute() { + RadioButtonPreference rbPref = new RadioButtonPreference(mContext); + rbPref.setKey(PreventRingingGesturePreferenceController.KEY_MUTE); + + Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.VOLUME_HUSH_GESTURE, + Settings.Secure.VOLUME_HUSH_OFF); + mController.onRadioButtonClicked(rbPref); + + assertThat(Settings.Secure.VOLUME_HUSH_MUTE).isEqualTo( + Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.VOLUME_HUSH_GESTURE, Settings.Secure.VOLUME_HUSH_OFF)); + } + + @Test + public void testRadioButtonClicked_vibrate() { + RadioButtonPreference rbPref = new RadioButtonPreference(mContext); + rbPref.setKey(PreventRingingGesturePreferenceController.KEY_VIBRATE); + + Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.VOLUME_HUSH_GESTURE, + Settings.Secure.VOLUME_HUSH_OFF); + mController.onRadioButtonClicked(rbPref); + + assertThat(Settings.Secure.VOLUME_HUSH_VIBRATE).isEqualTo( + Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.VOLUME_HUSH_GESTURE, Settings.Secure.VOLUME_HUSH_OFF)); + } + + @Test + public void testRadioButtonClicked_off() { + RadioButtonPreference rbPref = new RadioButtonPreference(mContext); + rbPref.setKey(PreventRingingGesturePreferenceController.KEY_NONE); + + Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.VOLUME_HUSH_GESTURE, + Settings.Secure.VOLUME_HUSH_MUTE); + + mController.onRadioButtonClicked(rbPref); + + assertThat(Settings.Secure.VOLUME_HUSH_OFF).isEqualTo( + Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.VOLUME_HUSH_GESTURE, Settings.Secure.VOLUME_HUSH_VIBRATE)); + } +} diff --git a/tests/robotests/src/com/android/settings/gestures/PreventRingingPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/gestures/PreventRingingPreferenceControllerTest.java deleted file mode 100644 index 65c3b5df5dc..00000000000 --- a/tests/robotests/src/com/android/settings/gestures/PreventRingingPreferenceControllerTest.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2018 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 static android.provider.Settings.Secure.VOLUME_HUSH_GESTURE; -import static android.provider.Settings.Secure.VOLUME_HUSH_MUTE; -import static android.provider.Settings.Secure.VOLUME_HUSH_OFF; - -import static com.google.common.truth.Truth.assertThat; - -import static junit.framework.Assert.assertEquals; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.content.Context; -import android.provider.Settings; - -import androidx.preference.ListPreference; -import androidx.preference.Preference; - -import com.android.settings.testutils.SettingsRobolectricTestRunner; - -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; - -@RunWith(SettingsRobolectricTestRunner.class) -public class PreventRingingPreferenceControllerTest { - - private static final String KEY_PICK_UP = "gesture_prevent_ringing"; - - @Mock(answer = Answers.RETURNS_DEEP_STUBS) - private Context mContext; - - private PreventRingingPreferenceController mController; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - mController = new PreventRingingPreferenceController(mContext, KEY_PICK_UP); - } - - @Test - public void testIsAvailable_configIsTrue_shouldReturnTrue() { - when(mContext.getResources().getBoolean( - com.android.internal.R.bool.config_volumeHushGestureEnabled)).thenReturn(true); - - assertThat(mController.isAvailable()).isTrue(); - } - - @Test - public void testIsAvailable_configIsFalse_shouldReturnFalse() { - when(mContext.getResources().getBoolean( - com.android.internal.R.bool.config_volumeHushGestureEnabled)).thenReturn(false); - - assertThat(mController.isAvailable()).isFalse(); - } - - @Test - public void testUpdateState_mute() { - ListPreference pref = mock(ListPreference.class); - Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.VOLUME_HUSH_GESTURE, - Settings.Secure.VOLUME_HUSH_MUTE); - mController.updateState(pref); - verify(pref).setValue(String.valueOf(Settings.Secure.VOLUME_HUSH_MUTE)); - } - - @Test - public void testUpdateState_vibrate() { - ListPreference pref = mock(ListPreference.class); - Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.VOLUME_HUSH_GESTURE, - Settings.Secure.VOLUME_HUSH_VIBRATE); - mController.updateState(pref); - verify(pref).setValue(String.valueOf(Settings.Secure.VOLUME_HUSH_VIBRATE)); - } - - @Test - public void testUpdateState_other() { - ListPreference pref = mock(ListPreference.class); - Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.VOLUME_HUSH_GESTURE, - 7); - mController.updateState(pref); - verify(pref).setValue(String.valueOf(Settings.Secure.VOLUME_HUSH_OFF)); - } - - @Test - public void testUpdateState_parentPage() { - Preference pref = mock(Preference.class); - // verify no exception - mController.updateState(pref); - } - - @Test - public void testOnPreferenceChange() { - Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.VOLUME_HUSH_GESTURE, - 7); - - mController.onPreferenceChange(mock(Preference.class), String.valueOf(VOLUME_HUSH_MUTE)); - - assertEquals(VOLUME_HUSH_MUTE, Settings.Secure.getInt(mContext.getContentResolver(), - VOLUME_HUSH_GESTURE, VOLUME_HUSH_OFF)); - } -} From d9c6692f25a8dce9144a387c3ed249521788ae2c Mon Sep 17 00:00:00 2001 From: Fan Zhang Date: Tue, 6 Nov 2018 15:21:07 -0800 Subject: [PATCH 4/8] Fix crash when starting daydream setting with wrong context The context must be a UI context. This is a new requirement in P. Change-Id: I3e2de5068f44010da7d57af030e9f029dd97a7f8 Merged-In: I28874f296da617f4cedf6706b0663b76671f1780 Fixes: 111375261 Fixes: 113312592 Test: robotests --- .../settings/dream/CurrentDreamPreferenceController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/android/settings/dream/CurrentDreamPreferenceController.java b/src/com/android/settings/dream/CurrentDreamPreferenceController.java index 86143dd6583..e053760141b 100644 --- a/src/com/android/settings/dream/CurrentDreamPreferenceController.java +++ b/src/com/android/settings/dream/CurrentDreamPreferenceController.java @@ -69,7 +69,7 @@ public class CurrentDreamPreferenceController extends AbstractPreferenceControll private void launchScreenSaverSettings() { Optional info = getActiveDreamInfo(); if (!info.isPresent()) return; - mBackend.launchSettings(info.get()); + mBackend.launchSettings(mContext, info.get()); } private Optional getActiveDreamInfo() { From 9a28c433f2a37315c828741d13605fc995046360 Mon Sep 17 00:00:00 2001 From: Fan Zhang Date: Tue, 6 Nov 2018 13:46:03 -0800 Subject: [PATCH 5/8] Update proto for contextual_card_list. Bug: 118842350 Test: rebuild Change-Id: I838738f56c8793a8e4d6adf43bc812777edbcd88 --- libs/contextualcards.aar | Bin 2862 -> 5481 bytes protos/contextual_card_list.proto | 24 +++++++++++++----- .../SettingsContextualCardProvider.java | 7 +++-- .../SettingsContextualCardProviderTest.java | 20 ++++++++++++--- 4 files changed, 37 insertions(+), 14 deletions(-) diff --git a/libs/contextualcards.aar b/libs/contextualcards.aar index 5e855fa0dec7120b31ca996bd877b25e3dd86ad6..892a6be26e64e6681cc0efd4cd6a3807a877e2e1 100755 GIT binary patch delta 4998 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 Q08Nt+3m67;6aWAK0OicGwg3PC delta 2371 zcmV-J3B2~{Dy|l=3ITt;mIqK%*&fCdnjsJ#O_U}CLWxL%f(l3xSy&;28bv9=Ktc$F zViG_?kq6QgK?q6{ktQX8NGB``0!vqHC^i%B~=gyhA=g#;2 z{^y=^@0rgQ#mff<06>7%1~mYD)dT^20JIs#MAO3B9J$g90N8(WNIzkQclWQ}t#!%O z{zZy5v9>TbbHE_b=4hNB$&)~(X&xj{s3dfae zd=KWg<>t;Z`fk>`t^18F2>^jW0N^^d(?4dLei@stj!l18zhI(2B?RF?#!;y;wrHE{ zdqs1^@*?v;XuiFumNeU$tyUhRV`BO6dN@4Wv!^<{)>-NJK(ukI0^vLbAOhRs8T7P2 zdm!}PJN6<3fDD*xJP65#2KGpkd_OcDApksjp%`x;_H? zX5)|JCt~U;@Jl<=wN>a&vV9dWd5}$eYP;lp_}C+$P|QYFIaaKUX%FAGq%iqFJ+qvY zC&kYgTofJc*ml_4s*KOKUt9V0woOBpIj^iz-HU(lTWLxesWsq7B|O)f^`sKcnC+q)HhMI+ML z=dI#yCvJBAFxaohp8golrh6{4bB#aoJX2bRKMMj& zj5enHpdBMMWUm{!2%9YokO}U5-okekr~7}C3rX`{-%O_^({-y(?~GI3uzUB=1#jmp zZ!b?bUsk=}`%RlwQRgNavnDEiV!Nb8r0yT=t?Ts(VxC^6*SU~tnHm+AW9E4n3-@T~ zN|E2>?9SM6tB4#E_+dkyjIs$;x3|0YJoY2WM+;$@OzEktic>8}%wsiHdlh!dDcgU0 zqb%`&*SDLu2cQ$x=obo=b4pP zdzi1z&5lckx9Mm)QUiMp+J~L?jzNC}&drF=%wA!QO2>9@%7bav;Hd*IcZ^_OKG05n z^hnrQ8yMB&8aObtX_vW%;Z|uBT;a%)j^mV*Gm%1(JW}?R4SE%iXjqdeRXo{ig=+Wc(}`n~W{O4J;7PE=}_K z^&PG-n452_%Dp)v#mKA)N~W+F1t~Z9&6_HTk1#NgP;8_zzOhPodg5*3=2vCa1ziVj zrW0gm?T+&%J{8#D!fc$GkMi|du(tJ_mo=kbh8P4VFB}4mnA>S#mu*bo z#S?60lpDg%d(>1{n38`SBo=?kh($CuV|#?Y@0xIkNhr-9VZiJrdd8)*!-4M^n2_{{ zG5cd8bGQ8IC6P-esq2rNKNYSh|{MZK%`crNK;!gdQrn@ZShJ3@@C6L~tW z^=WFA-Uz!&9qE8jPCcTeB#1zzYBWwVbh-}-?iYu`8dQ49b2GHWQ(E_*X)P(tPPAk_ zNh4H6BEx&xh2nog>I`UFFzjV#-jM7ln0Z0*rn*UavxruhS)h#mo0btHDsubLIevS` zv*hJpMO3e~Ck)H~MBTYdo(*xhV;SVFUmqR4r`t-yxK~gdap6q;5=no{po?Ll9*NPM zTw>U4a!0;6^75FEbgPSKs~nhewWDnOHCaR@O)!K!*gzgIfP0eUZ0m9*r*(a@ z{Ih8QzMV7wsyJt9IPh~;8b;2flp zzag#5dfZj92o9_&&-Z6P?rb9tHs!n7|D6SaJd%JvL&Vqd!il5SjelJ0ud?C#d3^oX zaLyy0?|&I>Y*G9IYgL3+uY6trU_W?m^dC@52MCw}WoEVr0000Ek>4DXGYUNr0{~D< z2M7QF0006200000lX4VO3YY<9X0`|b000h?&lE`r&Ao00000 p00000000043IG67E_8TwP)h{{000001ONm8-v9srISK#(007`yY6<`V diff --git a/protos/contextual_card_list.proto b/protos/contextual_card_list.proto index 89e6c138640..ea82408d893 100644 --- a/protos/contextual_card_list.proto +++ b/protos/contextual_card_list.proto @@ -8,12 +8,24 @@ message ContextualCardList { } message ContextualCard { - // Slice uri of the contextual card + + /** + * The category of a card: this is a hint for how a card should be collected, + * ranked, and presented + */ + enum Category { + DEFAULT = 0; + SUGGESTION = 1; + POSSIBLE = 2; + IMPORTANT = 3; + EXCLUSIVE = 4; + } + + /** Slice uri of the contextual card */ optional string sliceUri = 1; - // {@link ContextualCardCategory}. - optional int32 category = 2; - - // Name of the card. It should be identical in every app + /** Name of the card. It should be identical in every app */ optional string cardName = 3; -} \ No newline at end of file + + optional Category card_category = 4; +} diff --git a/src/com/android/settings/homepage/contextualcards/SettingsContextualCardProvider.java b/src/com/android/settings/homepage/contextualcards/SettingsContextualCardProvider.java index 59c9ba95b77..c736c4d1487 100644 --- a/src/com/android/settings/homepage/contextualcards/SettingsContextualCardProvider.java +++ b/src/com/android/settings/homepage/contextualcards/SettingsContextualCardProvider.java @@ -26,7 +26,6 @@ import com.android.settings.intelligence.ContextualCardProto.ContextualCard; import com.android.settings.intelligence.ContextualCardProto.ContextualCardList; import com.android.settings.wifi.WifiSlice; -import com.google.android.settings.intelligence.libs.contextualcards.ContextualCardCategory; import com.google.android.settings.intelligence.libs.contextualcards.ContextualCardProvider; /** Provides dynamic card for SettingsIntelligence. */ @@ -41,19 +40,19 @@ public class SettingsContextualCardProvider extends ContextualCardProvider { ContextualCard.newBuilder() .setSliceUri(WifiSlice.WIFI_URI.toString()) .setCardName(KEY_WIFI) - .setCategory(ContextualCardCategory.IMPORTANT) + .setCardCategory(ContextualCard.Category.IMPORTANT) .build(); final ContextualCard batteryInfoCard = ContextualCard.newBuilder() .setSliceUri(BatterySlice.BATTERY_CARD_URI.toString()) .setCardName(BatterySlice.PATH_BATTERY_INFO) - .setCategory(ContextualCardCategory.DEFAULT) + .setCardCategory(ContextualCard.Category.DEFAULT) .build(); final ContextualCard connectedDeviceCard = ContextualCard.newBuilder() .setSliceUri(ConnectedDeviceSlice.CONNECTED_DEVICE_URI.toString()) .setCardName(ConnectedDeviceSlice.PATH_CONNECTED_DEVICE) - .setCategory(ContextualCardCategory.IMPORTANT) + .setCardCategory(ContextualCard.Category.IMPORTANT) .build(); final ContextualCardList cards = ContextualCardList.newBuilder() .addCard(wifiCard) diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/SettingsContextualCardProviderTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/SettingsContextualCardProviderTest.java index 8b541c6455e..6b1f8b12da3 100644 --- a/tests/robotests/src/com/android/settings/homepage/contextualcards/SettingsContextualCardProviderTest.java +++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/SettingsContextualCardProviderTest.java @@ -19,7 +19,12 @@ package com.android.settings.homepage.contextualcards; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; + +import android.app.slice.SliceManager; import android.content.ContentResolver; +import android.content.Context; import android.net.Uri; import android.os.Bundle; @@ -28,30 +33,37 @@ import com.android.settings.intelligence.ContextualCardProto.ContextualCardList; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.wifi.WifiSlice; -import com.google.android.settings.intelligence.libs.contextualcards.ContextualCardCategory; import com.google.android.settings.intelligence.libs.contextualcards.ContextualCardProvider; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; import org.robolectric.Robolectric; import org.robolectric.RuntimeEnvironment; @RunWith(SettingsRobolectricTestRunner.class) public class SettingsContextualCardProviderTest { + @Mock + private SliceManager mSliceManager; private ContentResolver mResolver; private Uri mUri; private SettingsContextualCardProvider mProvider; @Before public void setUp() { + MockitoAnnotations.initMocks(this); mResolver = RuntimeEnvironment.application.getContentResolver(); mUri = new Uri.Builder() .scheme(ContentResolver.SCHEME_CONTENT) .authority(SettingsContextualCardProvider.CARD_AUTHORITY) .build(); - mProvider = Robolectric.setupContentProvider(SettingsContextualCardProvider.class); + mProvider = spy(Robolectric.setupContentProvider(SettingsContextualCardProvider.class)); + final Context context = spy(RuntimeEnvironment.application); + doReturn(mSliceManager).when(context).getSystemService(SliceManager.class); + doReturn(context).when(mProvider).getContext(); } @Test @@ -59,7 +71,7 @@ public class SettingsContextualCardProviderTest { final int actualNo = mProvider.getContextualCards().getCardCount(); final Bundle returnValue = - mResolver.call(mUri, ContextualCardProvider.METHOD_GET_CARD_LIST, "", null); + mProvider.call(ContextualCardProvider.METHOD_GET_CARD_LIST, "", null); final ContextualCardList cards = ContextualCardList.parseFrom( returnValue.getByteArray(ContextualCardProvider.BUNDLE_CARD_LIST)); @@ -76,6 +88,6 @@ public class SettingsContextualCardProviderTest { } } - assertThat(wifiCard.getCategory()).isEqualTo(ContextualCardCategory.IMPORTANT); + assertThat(wifiCard.getCardCategory()).isEqualTo(ContextualCard.Category.IMPORTANT); } } \ No newline at end of file From d20641059fd52e0e51d434c8b2fdd3bf4a97c96f Mon Sep 17 00:00:00 2001 From: Mill Chen Date: Fri, 2 Nov 2018 16:27:23 +0800 Subject: [PATCH 6/8] Adjust lookup table mechanism to support new UI of conditional card Use layout resource id as the return value of getItemViewType in the ContextualCardsAdapter to make sure the RecyclerView could work normally, and adjust the lookup table mechanism to meet the current design as well. Bug: 113451905, 112578070 Test: visual, robotest Change-Id: I8fa299e44025a0b71b6990d020e7f0683c153337 --- .../ContextualCardLookupTable.java | 55 +++++-- .../ContextualCardRenderer.java | 4 +- .../ContextualCardsAdapter.java | 22 +-- .../ControllerRendererPool.java | 28 +++- .../ConditionContextualCardRenderer.java | 5 +- .../slices/SliceContextualCardRenderer.java | 5 +- .../ContextualCardLookupTableTest.java | 115 +++++++++++++++ .../ControllerRendererPoolTest.java | 137 ++++++++++++++++++ .../ConditionContextualCardRendererTest.java | 4 +- .../SliceContextualCardRendererTest.java | 2 +- 10 files changed, 339 insertions(+), 38 deletions(-) create mode 100644 tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardLookupTableTest.java create mode 100644 tests/robotests/src/com/android/settings/homepage/contextualcards/ControllerRendererPoolTest.java diff --git a/src/com/android/settings/homepage/contextualcards/ContextualCardLookupTable.java b/src/com/android/settings/homepage/contextualcards/ContextualCardLookupTable.java index 0268fb097c8..2ac0ad6a910 100644 --- a/src/com/android/settings/homepage/contextualcards/ContextualCardLookupTable.java +++ b/src/com/android/settings/homepage/contextualcards/ContextualCardLookupTable.java @@ -16,43 +16,57 @@ package com.android.settings.homepage.contextualcards; +import android.util.Log; + +import androidx.annotation.VisibleForTesting; + import com.android.settings.homepage.contextualcards.ContextualCard.CardType; import com.android.settings.homepage.contextualcards.conditional.ConditionContextualCardController; import com.android.settings.homepage.contextualcards.conditional.ConditionContextualCardRenderer; import com.android.settings.homepage.contextualcards.slices.SliceContextualCardController; import com.android.settings.homepage.contextualcards.slices.SliceContextualCardRenderer; +import java.util.Comparator; +import java.util.List; import java.util.Set; import java.util.TreeSet; +import java.util.stream.Collectors; public class ContextualCardLookupTable { - + private static final String TAG = "ContextualCardLookup"; static class ControllerRendererMapping implements Comparable { @CardType - private final int mCardType; - private final Class mControllerClass; - private final Class mRendererClass; + final int mCardType; + final int mViewType; + final Class mControllerClass; + final Class mRendererClass; - private ControllerRendererMapping(@CardType int cardType, + ControllerRendererMapping(@CardType int cardType, int viewType, Class controllerClass, Class rendererClass) { mCardType = cardType; + mViewType = viewType; mControllerClass = controllerClass; mRendererClass = rendererClass; } @Override public int compareTo(ControllerRendererMapping other) { - return Integer.compare(this.mCardType, other.mCardType); + return Comparator.comparingInt((ControllerRendererMapping mapping) -> mapping.mCardType) + .thenComparingInt(mapping -> mapping.mViewType) + .compare(this, other); } } - private static final Set LOOKUP_TABLE = + @VisibleForTesting + static final Set LOOKUP_TABLE = new TreeSet() {{ add(new ControllerRendererMapping(CardType.CONDITIONAL, + ConditionContextualCardRenderer.VIEW_TYPE, ConditionContextualCardController.class, ConditionContextualCardRenderer.class)); add(new ControllerRendererMapping(CardType.SLICE, + SliceContextualCardRenderer.VIEW_TYPE, SliceContextualCardController.class, SliceContextualCardRenderer.class)); }}; @@ -67,14 +81,27 @@ public class ContextualCardLookupTable { return null; } - //TODO(b/112578070): Implement multi renderer cases. - public static Class getCardRendererClasses( + public static Class getCardRendererClassByCardType( @CardType int cardType) { - for (ControllerRendererMapping mapping : LOOKUP_TABLE) { - if (mapping.mCardType == cardType) { - return mapping.mRendererClass; - } + return LOOKUP_TABLE.stream() + .filter(m -> m.mCardType == cardType) + .findFirst() + .map(mapping -> mapping.mRendererClass) + .orElse(null); + } + + public static Class getCardRendererClassByViewType( + int viewType) throws IllegalStateException { + List validMappings = LOOKUP_TABLE.stream() + .filter(m -> m.mViewType == viewType).collect(Collectors.toList()); + if (validMappings == null || validMappings.isEmpty()) { + Log.w(TAG, "No matching mapping"); + return null; } - return null; + if (validMappings.size() != 1) { + throw new IllegalStateException("Have duplicate VIEW_TYPE in lookup table."); + } + + return validMappings.get(0).mRendererClass; } } diff --git a/src/com/android/settings/homepage/contextualcards/ContextualCardRenderer.java b/src/com/android/settings/homepage/contextualcards/ContextualCardRenderer.java index 689b57232c2..bb85bd23916 100644 --- a/src/com/android/settings/homepage/contextualcards/ContextualCardRenderer.java +++ b/src/com/android/settings/homepage/contextualcards/ContextualCardRenderer.java @@ -26,9 +26,9 @@ import androidx.recyclerview.widget.RecyclerView; public interface ContextualCardRenderer { /** - * The layout type of the controller. + * The layout type of the renderer. */ - int getViewType(); + int getViewType(boolean isHalfWidth); /** * When {@link ContextualCardsAdapter} calls {@link ContextualCardsAdapter#onCreateViewHolder}, diff --git a/src/com/android/settings/homepage/contextualcards/ContextualCardsAdapter.java b/src/com/android/settings/homepage/contextualcards/ContextualCardsAdapter.java index 865d2421361..0a8749d37d4 100644 --- a/src/com/android/settings/homepage/contextualcards/ContextualCardsAdapter.java +++ b/src/com/android/settings/homepage/contextualcards/ContextualCardsAdapter.java @@ -61,26 +61,26 @@ public class ContextualCardsAdapter extends RecyclerView.Adapter getControllers() { + @VisibleForTesting + Set getControllers() { return mControllers; } - public ContextualCardRenderer getRenderer(Context context, LifecycleOwner lifecycleOwner, - @ContextualCard.CardType int cardType) { + @VisibleForTesting + Set getRenderers() { + return mRenderers; + } + + public ContextualCardRenderer getRendererByViewType(Context context, + LifecycleOwner lifecycleOwner, int viewType) { final Class clz = - ContextualCardLookupTable.getCardRendererClasses(cardType); + ContextualCardLookupTable.getCardRendererClassByViewType(viewType); + return getRenderer(context, lifecycleOwner, clz); + } + + public ContextualCardRenderer getRendererByCardType(Context context, + LifecycleOwner lifecycleOwner, @ContextualCard.CardType int cardType) { + final Class clz = + ContextualCardLookupTable.getCardRendererClassByCardType(cardType); + return getRenderer(context, lifecycleOwner, clz); + } + + private ContextualCardRenderer getRenderer(Context context, LifecycleOwner lifecycleOwner, + @NonNull Class clz) { for (ContextualCardRenderer renderer : mRenderers) { if (renderer.getClass() == clz) { Log.d(TAG, "Renderer is already there."); diff --git a/src/com/android/settings/homepage/contextualcards/conditional/ConditionContextualCardRenderer.java b/src/com/android/settings/homepage/contextualcards/conditional/ConditionContextualCardRenderer.java index 8a3635be443..40986363046 100644 --- a/src/com/android/settings/homepage/contextualcards/conditional/ConditionContextualCardRenderer.java +++ b/src/com/android/settings/homepage/contextualcards/conditional/ConditionContextualCardRenderer.java @@ -37,6 +37,7 @@ import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; * Card renderer for {@link ConditionalContextualCard}. */ public class ConditionContextualCardRenderer implements ContextualCardRenderer { + public static final int VIEW_TYPE = R.layout.homepage_condition_tile; private final Context mContext; private final ControllerRendererPool mControllerRendererPool; @@ -48,8 +49,8 @@ public class ConditionContextualCardRenderer implements ContextualCardRenderer { } @Override - public int getViewType() { - return R.layout.homepage_condition_tile; + public int getViewType(boolean isHalfWidth) { + return VIEW_TYPE; } @Override diff --git a/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardRenderer.java b/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardRenderer.java index c2bf5360132..5fc4473dd16 100644 --- a/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardRenderer.java +++ b/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardRenderer.java @@ -45,6 +45,7 @@ import java.util.Map; */ public class SliceContextualCardRenderer implements ContextualCardRenderer, SliceView.OnSliceActionListener { + public static final int VIEW_TYPE = R.layout.homepage_slice_tile; private static final String TAG = "SliceCardRenderer"; @@ -61,8 +62,8 @@ public class SliceContextualCardRenderer implements ContextualCardRenderer, } @Override - public int getViewType() { - return R.layout.homepage_slice_tile; + public int getViewType(boolean isHalfWidth) { + return VIEW_TYPE; } @Override diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardLookupTableTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardLookupTableTest.java new file mode 100644 index 00000000000..4724500a294 --- /dev/null +++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardLookupTableTest.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2018 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.homepage.contextualcards; + +import static com.google.common.truth.Truth.assertThat; + +import com.android.settings.homepage.contextualcards.ContextualCardLookupTable + .ControllerRendererMapping; +import com.android.settings.testutils.SettingsRobolectricTestRunner; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.ArrayList; +import java.util.List; + +@RunWith(SettingsRobolectricTestRunner.class) +public class ContextualCardLookupTableTest { + + private static final int UNSUPPORTED_CARD_TYPE = -99999; + private static final int UNSUPPORTED_VIEW_TYPE = -99999; + + private List mOriginalLookupTable; + + @Before + public void setUp() { + mOriginalLookupTable = new ArrayList<>(); + ContextualCardLookupTable.LOOKUP_TABLE.stream() + .forEach(mapping -> mOriginalLookupTable.add(mapping)); + } + + @After + public void reset() { + ContextualCardLookupTable.LOOKUP_TABLE.clear(); + ContextualCardLookupTable.LOOKUP_TABLE.addAll(mOriginalLookupTable); + } + + @Test + public void getCardControllerClass_hasSupportedCardType_shouldGetCorrespondingController() { + for (ControllerRendererMapping mapping : ContextualCardLookupTable.LOOKUP_TABLE) { + assertThat(ContextualCardLookupTable.getCardControllerClass(mapping.mCardType)) + .isEqualTo(mapping.mControllerClass); + } + } + + @Test + public void getCardControllerClass_hasUnsupportedCardType_shouldAlwaysGetNull() { + assertThat(ContextualCardLookupTable.getCardControllerClass(UNSUPPORTED_CARD_TYPE)) + .isNull(); + } + + @Test + public void + getCardRendererClassByViewType_hasSupportedViewType_shouldGetCorrespondingRenderer() { + for (ControllerRendererMapping mapping : ContextualCardLookupTable.LOOKUP_TABLE) { + assertThat(ContextualCardLookupTable.getCardRendererClassByViewType(mapping.mViewType)) + .isEqualTo(mapping.mRendererClass); + } + } + + @Test + public void getCardRendererClassByViewType_hasUnsupportedViewType_shouldAlwaysGetNull() { + assertThat(ContextualCardLookupTable.getCardRendererClassByViewType( + UNSUPPORTED_VIEW_TYPE)).isNull(); + } + + @Test(expected = IllegalStateException.class) + public void + getCardRendererClassByViewType_hasDuplicateViewType_shouldThrowsIllegalStateException() { + final ControllerRendererMapping mapping1 = + new ControllerRendererMapping( + 1111 /* cardType */, UNSUPPORTED_VIEW_TYPE /* viewType */, + ContextualCardController.class, ContextualCardRenderer.class + ); + final ControllerRendererMapping mapping2 = + new ControllerRendererMapping( + 2222 /* cardType */, UNSUPPORTED_VIEW_TYPE /* viewType */, + ContextualCardController.class, ContextualCardRenderer.class + ); + ContextualCardLookupTable.LOOKUP_TABLE.add(mapping1); + ContextualCardLookupTable.LOOKUP_TABLE.add(mapping2); + + ContextualCardLookupTable.getCardRendererClassByViewType(UNSUPPORTED_VIEW_TYPE); + } + + @Test + public void getRendererClassByCardType_hasSupportedCardType_shouldGetCorrespondingRenderer() { + for (ControllerRendererMapping mapping : ContextualCardLookupTable.LOOKUP_TABLE) { + assertThat(ContextualCardLookupTable.getCardRendererClassByCardType(mapping.mCardType)) + .isEqualTo(mapping.mRendererClass); + } + } + + @Test + public void getCardRendererClassByCardType_hasUnsupportedCardType_shouldAlwaysGetNull() { + assertThat(ContextualCardLookupTable.getCardRendererClassByCardType(UNSUPPORTED_CARD_TYPE)) + .isNull(); + } +} diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/ControllerRendererPoolTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/ControllerRendererPoolTest.java new file mode 100644 index 00000000000..e51169e3972 --- /dev/null +++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/ControllerRendererPoolTest.java @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2018 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.homepage.contextualcards; + +import static com.google.common.truth.Truth.assertThat; + +import android.content.Context; + +import androidx.lifecycle.LifecycleOwner; + +import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settingslib.core.lifecycle.Lifecycle; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RuntimeEnvironment; + +@RunWith(SettingsRobolectricTestRunner.class) +public class ControllerRendererPoolTest { + + private static final int UNSUPPORTED_CARD_TYPE = -99999; + private static final int UNSUPPORTED_VIEW_TYPE = -99999; + + private ControllerRendererPool mPool; + private Context mContext; + private Lifecycle mLifecycle; + private LifecycleOwner mLifecycleOwner; + + @Before + public void setUp() { + mContext = RuntimeEnvironment.application; + mLifecycleOwner = () -> mLifecycle; + mLifecycle = new Lifecycle(mLifecycleOwner); + + mPool = new ControllerRendererPool(); + } + + @Test + public void getController_hasSupportedCardType_shouldReturnCorrespondingController() { + ContextualCardLookupTable.LOOKUP_TABLE.stream().forEach(mapping -> assertThat( + mPool.getController(mContext, mapping.mCardType).getClass()).isEqualTo( + mapping.mControllerClass)); + } + + @Test + public void getController_hasSupportedCardType_shouldHaveTwoControllersInPool() { + final long count = ContextualCardLookupTable.LOOKUP_TABLE.stream().map( + mapping -> mapping.mControllerClass).distinct().count(); + + ContextualCardLookupTable.LOOKUP_TABLE.stream().forEach( + mapping -> mPool.getController(mContext, mapping.mCardType)); + + assertThat(mPool.getControllers()).hasSize((int) count); + } + + @Test + public void getController_hasUnsupportedCardType_shouldReturnNullAndPoolIsEmpty() { + final ContextualCardController controller = mPool.getController(mContext, + UNSUPPORTED_CARD_TYPE); + + assertThat(controller).isNull(); + assertThat(mPool.getControllers()).isEmpty(); + } + + @Test + public void getRenderer_hasSupportedViewType_shouldReturnCorrespondingRenderer() { + ContextualCardLookupTable.LOOKUP_TABLE.stream().forEach(mapping -> assertThat( + mPool.getRendererByViewType(mContext, mLifecycleOwner, + mapping.mViewType).getClass()).isEqualTo(mapping.mRendererClass)); + } + + @Test + public void getRenderer_hasSupportedViewType_shouldHaveDistinctRenderersInPool() { + final long count = ContextualCardLookupTable.LOOKUP_TABLE.stream().map( + mapping -> mapping.mRendererClass).distinct().count(); + + ContextualCardLookupTable.LOOKUP_TABLE.stream().forEach( + mapping -> mPool.getRendererByViewType(mContext, mLifecycleOwner, + mapping.mViewType)); + + assertThat(mPool.getRenderers()).hasSize((int) count); + } + + @Test + public void getRenderer_hasUnsupportedViewType_shouldReturnNullAndPoolIsEmpty() { + final ContextualCardRenderer renderer = mPool.getRendererByViewType(mContext, + mLifecycleOwner, + UNSUPPORTED_VIEW_TYPE); + + assertThat(renderer).isNull(); + assertThat(mPool.getRenderers()).isEmpty(); + } + + @Test + public void getRenderer_hasSupportedCardTypeAndWidth_shouldReturnCorrespondingRenderer() { + ContextualCardLookupTable.LOOKUP_TABLE.stream().forEach(mapping -> assertThat( + mPool.getRendererByCardType(mContext, mLifecycleOwner, + mapping.mCardType).getClass()).isEqualTo(mapping.mRendererClass)); + } + + @Test + public void getRenderer_hasSupportedCardTypeAndWidth_shouldHaveDistinctRenderersInPool() { + final long count = ContextualCardLookupTable.LOOKUP_TABLE.stream().map( + mapping -> mapping.mRendererClass).distinct().count(); + + ContextualCardLookupTable.LOOKUP_TABLE.stream().forEach( + mapping -> mPool.getRendererByCardType(mContext, mLifecycleOwner, + mapping.mCardType)); + + assertThat(mPool.getRenderers()).hasSize((int) count); + } + + @Test + public void getRenderer_hasUnsupportedCardType_shouldReturnNullAndPoolIsEmpty() { + final ContextualCardRenderer renderer = mPool.getRendererByCardType(mContext, + mLifecycleOwner, + UNSUPPORTED_CARD_TYPE); + + assertThat(renderer).isNull(); + assertThat(mPool.getRenderers()).isEmpty(); + } +} diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/conditional/ConditionContextualCardRendererTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/conditional/ConditionContextualCardRendererTest.java index ccfbbfb2fe8..376042d6588 100644 --- a/tests/robotests/src/com/android/settings/homepage/contextualcards/conditional/ConditionContextualCardRendererTest.java +++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/conditional/ConditionContextualCardRendererTest.java @@ -61,7 +61,7 @@ public class ConditionContextualCardRendererTest { @Test public void bindView_shouldSetListener() { - final int viewType = mRenderer.getViewType(); + final int viewType = mRenderer.getViewType(false /* isHalfWidth */); final RecyclerView recyclerView = new RecyclerView(mContext); recyclerView.setLayoutManager(new LinearLayoutManager(mContext)); final View view = LayoutInflater.from(mContext).inflate(viewType, recyclerView, false); @@ -78,7 +78,7 @@ public class ConditionContextualCardRendererTest { @Test public void viewClick_shouldInvokeControllerPrimaryClick() { - final int viewType = mRenderer.getViewType(); + final int viewType = mRenderer.getViewType(false /* isHalfWidth */); final RecyclerView recyclerView = new RecyclerView(mContext); recyclerView.setLayoutManager(new LinearLayoutManager(mContext)); final View view = LayoutInflater.from(mContext).inflate(viewType, recyclerView, false); diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardRendererTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardRendererTest.java index 025f2623695..7a07d35dce3 100644 --- a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardRendererTest.java +++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardRendererTest.java @@ -113,7 +113,7 @@ public class SliceContextualCardRendererTest { } private RecyclerView.ViewHolder getSliceViewHolder() { - final int viewType = mRenderer.getViewType(); + final int viewType = mRenderer.getViewType(false /* isHalfWidth */); final RecyclerView recyclerView = new RecyclerView(mContext); recyclerView.setLayoutManager(new LinearLayoutManager(mContext)); final View view = LayoutInflater.from(mContext).inflate(viewType, recyclerView, false); From 2928cd4c722d076494c4189c81b5cc24826993b6 Mon Sep 17 00:00:00 2001 From: Raff Tsai Date: Mon, 5 Nov 2018 16:10:19 +0800 Subject: [PATCH 7/8] Fix Settings crash after disabling Settings Suggestion - Settings Suggestion App is responsible for searching, we can not prevent user disable it. Hide search feature if user disable it. Fixes: 118805907 Fixes: 117921464 Test: manual Change-Id: I61c47c52265a6efd79ef2fa60272bf6513e678b1 --- src/com/android/settings/Utils.java | 10 ++++++++ .../search/SearchFeatureProvider.java | 9 +++++++ .../actionbar/SearchMenuController.java | 10 +++++--- .../settings/SettingsActivityTest.java | 4 ++++ .../src/com/android/settings/UtilsTest.java | 24 +++++++++++++++++++ .../search/SearchFeatureProviderImplTest.java | 3 +++ .../actionbar/SearchMenuControllerTest.java | 3 +++ .../testutils/shadow/ShadowUtils.java | 9 +++++++ 8 files changed, 69 insertions(+), 3 deletions(-) diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java index 14a9e832f1b..79caefe0fad 100644 --- a/src/com/android/settings/Utils.java +++ b/src/com/android/settings/Utils.java @@ -979,4 +979,14 @@ public final class Utils extends com.android.settingslib.Utils { return packageManager.getDefaultActivityIcon(); } } + + /** Returns true if the current package is installed & enabled. */ + public static boolean isPackageEnabled(Context context, String packageName) { + try { + return context.getPackageManager().getApplicationInfo(packageName, 0).enabled; + } catch (Exception e) { + Log.e(TAG, "Error while retrieving application info for package " + packageName, e); + } + return false; + } } diff --git a/src/com/android/settings/search/SearchFeatureProvider.java b/src/com/android/settings/search/SearchFeatureProvider.java index b2eb8bbb3fc..9c1f07c9fa6 100644 --- a/src/com/android/settings/search/SearchFeatureProvider.java +++ b/src/com/android/settings/search/SearchFeatureProvider.java @@ -25,9 +25,11 @@ import android.content.Context; import android.content.Intent; import android.provider.Settings; import android.view.View; +import android.view.ViewGroup; import android.widget.Toolbar; import com.android.internal.logging.nano.MetricsProto; +import com.android.settings.Utils; import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.search.SearchIndexableResources; @@ -64,6 +66,13 @@ public interface SearchFeatureProvider { if (activity == null || toolbar == null) { return; } + if (!Utils.isPackageEnabled(activity, getSettingsIntelligencePkgName())) { + final ViewGroup parent = (ViewGroup)toolbar.getParent(); + if (parent != null) { + parent.setVisibility(View.GONE); + } + return; + } // Please forgive me for what I am about to do. // // Need to make the navigation icon non-clickable so that the entire card is clickable diff --git a/src/com/android/settings/search/actionbar/SearchMenuController.java b/src/com/android/settings/search/actionbar/SearchMenuController.java index 81e9e60d941..0caa3086918 100644 --- a/src/com/android/settings/search/actionbar/SearchMenuController.java +++ b/src/com/android/settings/search/actionbar/SearchMenuController.java @@ -56,9 +56,15 @@ public class SearchMenuController implements LifecycleObserver, OnCreateOptionsM @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + final Context context = mHost.getContext(); + final String SettingsIntelligencePkgName = FeatureFactory.getFactory(context) + .getSearchFeatureProvider().getSettingsIntelligencePkgName(); if (!Utils.isDeviceProvisioned(mHost.getContext())) { return; } + if (!Utils.isPackageEnabled(mHost.getContext(), SettingsIntelligencePkgName)) { + return; + } if (menu == null) { return; } @@ -72,10 +78,8 @@ public class SearchMenuController implements LifecycleObserver, OnCreateOptionsM searchItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); searchItem.setOnMenuItemClickListener(target -> { - final Context context = mHost.getContext(); final Intent intent = SearchFeatureProvider.SEARCH_UI_INTENT; - intent.setPackage(FeatureFactory.getFactory(mHost.getContext()) - .getSearchFeatureProvider().getSettingsIntelligencePkgName()); + intent.setPackage(SettingsIntelligencePkgName); FeatureFactory.getFactory(context).getMetricsFeatureProvider() .action(context, MetricsProto.MetricsEvent.ACTION_SEARCH_RESULTS); mHost.startActivityForResult(intent, 0 /* requestCode */); diff --git a/tests/robotests/src/com/android/settings/SettingsActivityTest.java b/tests/robotests/src/com/android/settings/SettingsActivityTest.java index af25f4835df..2c98f70c2b4 100644 --- a/tests/robotests/src/com/android/settings/SettingsActivityTest.java +++ b/tests/robotests/src/com/android/settings/SettingsActivityTest.java @@ -38,6 +38,7 @@ import androidx.fragment.app.FragmentTransaction; import com.android.settings.core.OnActivityResultListener; import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settings.testutils.shadow.ShadowUtils; import org.junit.Before; import org.junit.Test; @@ -46,6 +47,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.Robolectric; import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; import java.util.ArrayList; import java.util.List; @@ -69,6 +71,7 @@ public class SettingsActivityTest { } @Test + @Config(shadows = ShadowUtils.class) public void onCreate_deviceNotProvisioned_shouldDisableSearch() { Global.putInt(mContext.getContentResolver(), Global.DEVICE_PROVISIONED, 0); final SettingsActivity activity = Robolectric.buildActivity(SettingsActivity.class) @@ -80,6 +83,7 @@ public class SettingsActivityTest { } @Test + @Config(shadows = ShadowUtils.class) public void onCreate_deviceProvisioned_shouldEnableSearch() { Global.putInt(mContext.getContentResolver(), Global.DEVICE_PROVISIONED, 1); final SettingsActivity activity = Robolectric.buildActivity(SettingsActivity.class) diff --git a/tests/robotests/src/com/android/settings/UtilsTest.java b/tests/robotests/src/com/android/settings/UtilsTest.java index 5f2abba37ef..77cbae2b2bc 100644 --- a/tests/robotests/src/com/android/settings/UtilsTest.java +++ b/tests/robotests/src/com/android/settings/UtilsTest.java @@ -93,6 +93,7 @@ public class UtilsTest { when(mContext.getSystemService(WifiManager.class)).thenReturn(wifiManager); when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE)) .thenReturn(connectivityManager); + when(mContext.getPackageManager()).thenReturn(mPackageManager); } @Test @@ -200,4 +201,27 @@ public class UtilsTest { verify(mPackageManager).getApplicationInfoAsUser(eq(PACKAGE_NAME), anyInt(), eq(USER_ID)); verify(mIconDrawableFactory).getBadgedIcon(mApplicationInfo, USER_ID); } + + @Test + public void isPackageEnabled_appEnabled_returnTrue() + throws PackageManager.NameNotFoundException{ + mApplicationInfo.enabled = true; + when(mPackageManager.getApplicationInfo(PACKAGE_NAME, 0)).thenReturn(mApplicationInfo); + + assertThat(Utils.isPackageEnabled(mContext, PACKAGE_NAME)).isTrue(); + } + + @Test + public void isPackageEnabled_appDisabled_returnTrue() + throws PackageManager.NameNotFoundException{ + mApplicationInfo.enabled = false; + when(mPackageManager.getApplicationInfo(PACKAGE_NAME, 0)).thenReturn(mApplicationInfo); + + assertThat(Utils.isPackageEnabled(mContext, PACKAGE_NAME)).isFalse(); + } + + @Test + public void isPackageEnabled_noApp_returnFalse() { + assertThat(Utils.isPackageEnabled(mContext, PACKAGE_NAME)).isFalse(); + } } diff --git a/tests/robotests/src/com/android/settings/search/SearchFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/search/SearchFeatureProviderImplTest.java index ba29ea1a9e3..d0546b6c5a5 100644 --- a/tests/robotests/src/com/android/settings/search/SearchFeatureProviderImplTest.java +++ b/tests/robotests/src/com/android/settings/search/SearchFeatureProviderImplTest.java @@ -29,12 +29,14 @@ import android.widget.Toolbar; import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settings.testutils.shadow.ShadowUtils; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.Robolectric; import org.robolectric.Shadows; +import org.robolectric.annotation.Config; @RunWith(SettingsRobolectricTestRunner.class) public class SearchFeatureProviderImplTest { @@ -50,6 +52,7 @@ public class SearchFeatureProviderImplTest { } @Test + @Config(shadows = ShadowUtils.class) public void initSearchToolbar_shouldInitWithOnClickListener() { mProvider.initSearchToolbar(mActivity, null); // Should not crash. diff --git a/tests/robotests/src/com/android/settings/search/actionbar/SearchMenuControllerTest.java b/tests/robotests/src/com/android/settings/search/actionbar/SearchMenuControllerTest.java index 3ab4ab48602..b4076b5226b 100644 --- a/tests/robotests/src/com/android/settings/search/actionbar/SearchMenuControllerTest.java +++ b/tests/robotests/src/com/android/settings/search/actionbar/SearchMenuControllerTest.java @@ -30,6 +30,7 @@ import android.view.MenuItem; import com.android.settings.R; import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settings.testutils.shadow.ShadowUtils; import com.android.settingslib.core.lifecycle.ObservableFragment; import com.android.settingslib.core.lifecycle.ObservablePreferenceFragment; @@ -39,8 +40,10 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; @RunWith(SettingsRobolectricTestRunner.class) +@Config(shadows = ShadowUtils.class) public class SearchMenuControllerTest { @Mock diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUtils.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUtils.java index b09bc7434f0..092795601c7 100644 --- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUtils.java +++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUtils.java @@ -21,9 +21,13 @@ import android.content.Context; import android.hardware.fingerprint.FingerprintManager; import android.os.UserHandle; import android.os.UserManager; +import android.util.Log; import com.android.settings.Utils; +import com.android.settings.overlay.FeatureFactory; +import com.android.settings.search.SearchFeatureProviderImpl; +import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; @@ -102,6 +106,11 @@ public class ShadowUtils { return null; } + @Implementation + public static boolean isPackageEnabled(Context context, String packageName) { + return true; + } + public static void setApplicationLabel(String packageName, String appLabel) { if (sAppNameMap == null) { sAppNameMap = new HashMap<>(); From 0ca71304036fabf0a389d139a3e087ba7382b2a1 Mon Sep 17 00:00:00 2001 From: Mill Chen Date: Mon, 5 Nov 2018 17:20:22 +0800 Subject: [PATCH 8/8] Update new UI of conditional cards - Add half-width and full-width layouts for conditional cards - Keep origin strings of conditional cards, that could make conditional cards truncate its title and summary in half-width card. - Add default value of isHalfWidth to each condition controller. Bug: 113451905 Test: robotests, visual Change-Id: Ib0055f32c4ab9e73c0e0a57c6b0ef586d52942e0 --- res/layout/homepage_condition_full_tile.xml | 78 +++++++++++++++ res/layout/homepage_condition_half_tile.xml | 63 ++++++++++++ res/layout/homepage_condition_tile.xml | 97 ------------------- res/values/dimens.xml | 12 ++- res/values/styles.xml | 30 ++++++ .../contextualcards/ContextualCard.java | 25 +++++ .../ContextualCardLookupTable.java | 6 +- .../AirplaneModeConditionController.java | 1 + .../BackgroundDataConditionController.java | 1 + .../BatterySaverConditionController.java | 1 + .../CellularDataConditionController.java | 1 + .../ConditionContextualCardController.java | 8 ++ .../ConditionContextualCardRenderer.java | 19 ++-- .../DndConditionCardController.java | 1 + .../HotspotConditionController.java | 1 + .../NightDisplayConditionController.java | 1 + .../RingerMutedConditionController.java | 1 + .../RingerVibrateConditionController.java | 1 + .../WorkModeConditionController.java | 1 + 19 files changed, 237 insertions(+), 111 deletions(-) create mode 100644 res/layout/homepage_condition_full_tile.xml create mode 100644 res/layout/homepage_condition_half_tile.xml delete mode 100644 res/layout/homepage_condition_tile.xml diff --git a/res/layout/homepage_condition_full_tile.xml b/res/layout/homepage_condition_full_tile.xml new file mode 100644 index 00000000000..f00132b8507 --- /dev/null +++ b/res/layout/homepage_condition_full_tile.xml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + +