Add interstitial for modes that are disabled but not by user

This covers both the case of navigating to the mode page through the mode aggregator menu as well as if an app sends an intent to go to the mode.

The interstitial visuals are not done yet; in particular, the image is just a gray box for now.

Manual tests done:
- visually verifying the interstitial page in both portrait and landscape
- accessing the mode page from modes list, confirming interstitial pops up
- accessing enabled and disabled-by-user mode page from modes list (no interstitial)
- getting to the mode page from intent through an app
- accessing enabled and disabled-by-user mode pages from app intent (no interstitial)
- adjusted display and font size (it looks bad with max display & font size, maybe not much to be done)

Bug: 332730534
Test: manual, ZenModeFragmentTest, SetupInterstitialActivityTest
Flag: android.app.modes_ui
Change-Id: I21f13b0842d5b118a341f7d85e8fcac947ca3a06
This commit is contained in:
Yuri Lin
2024-07-18 17:12:41 -04:00
parent bc915e7bdc
commit 65548c0e56
8 changed files with 637 additions and 3 deletions

View File

@@ -0,0 +1,99 @@
/*
* Copyright (C) 2024 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.notification.modes;
import static android.provider.Settings.EXTRA_AUTOMATIC_ZEN_RULE_ID;
import static com.android.settings.SettingsActivity.EXTRA_SHOW_FRAGMENT;
import static com.android.settings.SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.robolectric.Shadows.shadowOf;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import androidx.test.core.app.ActivityScenario;
import com.android.settingslib.notification.modes.TestModeBuilder;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
@RunWith(RobolectricTestRunner.class)
public class SetupInterstitialActivityTest {
private static final String MODE_ID = "modeId";
@Mock
private ZenModesBackend mBackend;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
// set global backend instance so that when the interstitial activity launches, it'll get
// this mock backend
ZenModesBackend.setInstance(mBackend);
}
@Test
public void enableButton_enablesModeAndRedirectsToModePage() {
ZenMode mode = new TestModeBuilder().setId(MODE_ID).setEnabled(false).build();
when(mBackend.getMode(MODE_ID)).thenReturn(mode);
// Set up scenario with this mode information
ActivityScenario<SetupInterstitialActivity> scenario =
ActivityScenario.launch(new Intent(Intent.ACTION_MAIN)
.setClass(RuntimeEnvironment.getApplication(),
SetupInterstitialActivity.class)
.putExtra(EXTRA_AUTOMATIC_ZEN_RULE_ID, MODE_ID));
scenario.onActivity(activity -> {
View.OnClickListener listener = activity.enableButtonListener(MODE_ID);
// simulate button press even though we don't actually have a button
listener.onClick(null);
// verify that the backend got a request to enable the mode
ArgumentCaptor<ZenMode> captor = ArgumentCaptor.forClass(ZenMode.class);
verify(mBackend).updateMode(captor.capture());
ZenMode updatedMode = captor.getValue();
assertThat(updatedMode.getId()).isEqualTo(MODE_ID);
assertThat(updatedMode.isEnabled()).isTrue();
// confirm that the next activity is the mode page
Intent openModePageIntent = shadowOf(activity).getNextStartedActivity();
assertThat(openModePageIntent.getStringExtra(EXTRA_SHOW_FRAGMENT))
.isEqualTo(ZenModeFragment.class.getName());
Bundle fragmentArgs = openModePageIntent.getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
assertThat(fragmentArgs).isNotNull();
assertThat(fragmentArgs.getString(EXTRA_AUTOMATIC_ZEN_RULE_ID)).isEqualTo(MODE_ID);
});
scenario.close();
}
}

View File

@@ -0,0 +1,125 @@
/*
* Copyright (C) 2024 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.notification.modes;
import static android.provider.Settings.EXTRA_AUTOMATIC_ZEN_RULE_ID;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.when;
import static org.robolectric.Shadows.shadowOf;
import android.content.Intent;
import android.os.Bundle;
import androidx.fragment.app.testing.FragmentScenario;
import androidx.lifecycle.Lifecycle;
import com.android.settingslib.notification.modes.TestModeBuilder;
import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)
public class ZenModeFragmentTest {
private static final String MODE_ID = "modeId";
@Mock
private ZenModesBackend mBackend;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
// set up static instance so that the fragment will get a mock version of the backend
ZenModesBackend.setInstance(mBackend);
}
// Sets up the scenario's fragment by passing in arguments setting the provided mode ID.
// After running this method, users can then use scenario.onFragment(fragment -> {...}) on the
// returned scenario to test fragment behavior.
private FragmentScenario<ZenModeFragment> setUpScenarioForModeId(String modeId) {
Bundle args = new Bundle();
args.putString(EXTRA_AUTOMATIC_ZEN_RULE_ID, modeId);
return FragmentScenario.launch(
ZenModeFragment.class, /* bundle= */ args, 0, Lifecycle.State.INITIALIZED);
}
@Test
public void disabledMode_redirectsToInterstitial() {
// Mode is disabled, and not by the user
ZenMode mode = new TestModeBuilder().setId(MODE_ID).setEnabled(false, false)
.build();
when(mBackend.getMode(MODE_ID)).thenReturn(mode);
// actually set up fragment for testing
FragmentScenario scenario = setUpScenarioForModeId(MODE_ID);
scenario.moveToState(Lifecycle.State.STARTED);
scenario.onFragment(fragment -> {
// since the mode is disabled & not by the user, we should go to the next activity
Intent nextIntent = shadowOf(fragment.getActivity()).getNextStartedActivity();
assertThat(nextIntent).isNotNull();
assertThat(nextIntent.getComponent().getClassName()).isEqualTo(
SetupInterstitialActivity.class.getCanonicalName());
assertThat(nextIntent.getStringExtra(EXTRA_AUTOMATIC_ZEN_RULE_ID)).isEqualTo(MODE_ID);
});
scenario.close();
}
@Test
public void disabledMode_byUser_noRedirect() {
// Mode is disabled by the user
ZenMode mode = new TestModeBuilder().setId(MODE_ID).setEnabled(false, true)
.build();
when(mBackend.getMode(MODE_ID)).thenReturn(mode);
FragmentScenario scenario = setUpScenarioForModeId(MODE_ID);
scenario.moveToState(Lifecycle.State.STARTED);
scenario.onFragment(fragment -> {
// there shouldn't be a next started activity, because we don't redirect
Intent nextIntent = shadowOf(fragment.getActivity()).getNextStartedActivity();
assertThat(nextIntent).isNull();
});
scenario.close();
}
@Test
public void enabledMode_noRedirect() {
// enabled rule
ZenMode mode = new TestModeBuilder().setId(MODE_ID).setEnabled(true)
.build();
when(mBackend.getMode(MODE_ID)).thenReturn(mode);
FragmentScenario scenario = setUpScenarioForModeId(MODE_ID);
scenario.moveToState(Lifecycle.State.STARTED);
scenario.onFragment(fragment -> {
// there shouldn't be a next started activity, because we don't redirect
Intent nextIntent = shadowOf(fragment.getActivity()).getNextStartedActivity();
assertThat(nextIntent).isNull();
});
scenario.close();
}
}