Loading src/com/android/settings/overlay/SurveyFeatureProvider.java +24 −4 Original line number Diff line number Diff line Loading @@ -26,14 +26,15 @@ import androidx.localbroadcastmanager.content.LocalBroadcastManager; * An interface for classes wishing to provide the ability to serve surveys to implement. */ public interface SurveyFeatureProvider { /** * Downloads a survey asynchronously to shared preferences to be served at a later date. * * @param activity A valid context. * @param surveyId A unique Id representing a survey to download. * @param data a text blob to be attached to the survey results. * @deprecated This is not used after T. */ @Deprecated void downloadSurvey(Activity activity, String surveyId, @Nullable String data); /** Loading @@ -42,7 +43,9 @@ public interface SurveyFeatureProvider { * @param activity The host activity to show the survey in. * @param surveyId A unique Id representing a survey to download. * @return A boolean indicating if a survey was shown or not. * @deprecated This is not used after T. */ @Deprecated boolean showSurveyIfAvailable(Activity activity, String surveyId); /** Loading @@ -52,7 +55,9 @@ public interface SurveyFeatureProvider { * @param context A valid context. * @param simpleKey The simple name of the key to get the surveyId for. * @return The unique Id as a string or null on error. * @deprecated This is not used after T. */ @Deprecated String getSurveyId(Context context, String simpleKey); /** Loading @@ -64,24 +69,32 @@ public interface SurveyFeatureProvider { * @param surveyId the site ID. * @return the unix timestamp for the available survey for the given {@coe siteId} or -1 if * there is none available. * @deprecated This is not used after T. */ @Deprecated long getSurveyExpirationDate(Context context, String surveyId); /** * Registers an activity to show surveys/prompts as soon as they are downloaded. The receiver * should be unregistered prior to destroying the activity to avoid undefined behavior by * calling {@link #unregisterReceiver(Activity, BroadcastReceiver)}. * * @param activity The activity that should show surveys once they are downloaded. * @return the broadcast receiver listening for survey downloads. Must be unregistered before * leaving the activity. * @deprecated This is not used after T. */ @Deprecated BroadcastReceiver createAndRegisterReceiver(Activity activity); /** * Unregisters the broadcast receiver for this activity. Should only be called once per activity * after a call to {@link #createAndRegisterReceiver(Activity)}. * * @param activity The activity that was used to register the BroadcastReceiver. * @deprecated This is not used after T. */ @Deprecated static void unregisterReceiver(Activity activity, BroadcastReceiver receiver) { if (activity == null) { throw new IllegalStateException("Cannot unregister receiver if activity is null"); Loading @@ -89,4 +102,11 @@ public interface SurveyFeatureProvider { LocalBroadcastManager.getInstance(activity).unregisterReceiver(receiver); } /** * Send the visited activity to the place where it will trigger a survey if possible. * * @param simpleKey The simple name of the key to get the surveyId for. */ void sendActivityIfAvailable(String simpleKey); } src/com/android/settings/survey/SurveyMixin.java +5 −25 Original line number Diff line number Diff line Loading @@ -16,14 +16,12 @@ package com.android.settings.survey; import android.app.Activity; import android.content.BroadcastReceiver; import androidx.fragment.app.Fragment; import com.android.settings.overlay.FeatureFactory; import com.android.settings.overlay.SurveyFeatureProvider; import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.events.OnPause; import com.android.settingslib.core.lifecycle.events.OnResume; /** Loading @@ -31,16 +29,16 @@ import com.android.settingslib.core.lifecycle.events.OnResume; * in settings. This allows new classes to automatically support settings provided the extend * one of the relevant classes in com.android.settings.lifecycle. */ public class SurveyMixin implements LifecycleObserver, OnResume, OnPause { public class SurveyMixin implements LifecycleObserver, OnResume { private String mName; private Fragment mFragment; private BroadcastReceiver mReceiver; /** * A mixin that attempts to perform survey related tasks right before onResume is called * in a Settings PreferenceFragment. This will allow for remote updating and creation of * surveys. * * @param fragment The fragment that this mixin will be attached to. * @param fragmentName The simple name of the fragment. */ Loading @@ -53,31 +51,13 @@ public class SurveyMixin implements LifecycleObserver, OnResume, OnPause { public void onResume() { Activity activity = mFragment.getActivity(); // guard against the activity not existing yet or the feature being disabled // guard against the activity not existing yet if (activity != null) { SurveyFeatureProvider provider = FeatureFactory.getFactory(activity).getSurveyFeatureProvider(activity); if (provider != null) { // Try to download a survey if there is none available, show the survey otherwise String id = provider.getSurveyId(activity, mName); if (provider.getSurveyExpirationDate(activity, id) <= -1) { // register the receiver to show the survey on completion. mReceiver = provider.createAndRegisterReceiver(activity); provider.downloadSurvey(activity, id, null /* data */); } else { provider.showSurveyIfAvailable(activity, id); } } provider.sendActivityIfAvailable(mName); } } @Override public void onPause() { Activity activity = mFragment.getActivity(); if (mReceiver != null && activity != null) { SurveyFeatureProvider.unregisterReceiver(activity, mReceiver); mReceiver = null; } } } tests/robotests/src/com/android/settings/survey/SurveyMixinTest.java +2 −100 Original line number Diff line number Diff line package com.android.settings.survey; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.BroadcastReceiver; import android.content.Context; import android.content.IntentFilter; import androidx.fragment.app.FragmentActivity; import androidx.localbroadcastmanager.content.LocalBroadcastManager; import com.android.settings.core.InstrumentedPreferenceFragment; import com.android.settings.overlay.SurveyFeatureProvider; Loading @@ -28,22 +20,15 @@ import org.mockito.MockitoAnnotations; import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.util.ReflectionHelpers; import java.util.ArrayList; import java.util.HashMap; @RunWith(RobolectricTestRunner.class) public class SurveyMixinTest { private static final String FAKE_KEY = "fake_key"; private static final String FAKE_SURVEY_ID = "fake_id"; private Context mContext; private SurveyFeatureProvider mProvider; @Mock private BroadcastReceiver mReceiver; @Mock private InstrumentedPreferenceFragment mFragment; @Before Loading @@ -52,99 +37,16 @@ public class SurveyMixinTest { MockitoAnnotations.initMocks(this); mContext = RuntimeEnvironment.application; mProvider = FakeFeatureFactory.setupForTest().getSurveyFeatureProvider(mContext); when(mProvider.getSurveyId(any(), eq(FAKE_KEY))).thenReturn(FAKE_SURVEY_ID); } @Test public void onResume_triesRegisteringReceiverAndDownloadingWhenNoSurveyDetected() { // Pretend there is no survey in memory when(mProvider.getSurveyExpirationDate(any(), any())).thenReturn(-1L); public void onResume_noActionIfActivityDoesNotExist() { // Pretend we are an activity that is starting up FragmentActivity temp = Robolectric.setupActivity(FragmentActivity.class); when(mFragment.getActivity()).thenReturn(temp); SurveyMixin mixin = new SurveyMixin(mFragment, FAKE_KEY); mixin.onResume(); // Verify that a download was attempted verify(mProvider, times(1)).downloadSurvey(any(), any(), any()); // Verify that we registered a receiver for download completion broadcasts verify(mProvider, times(1)).createAndRegisterReceiver(any()); // Verify we did not try to show a survey verify(mProvider, never()).showSurveyIfAvailable(any(), any()); } @Test public void onResume_triesShowingSurveyWhenOneIsPresent() { // Pretend there is a survey in memory when(mProvider.getSurveyExpirationDate(any(), any())).thenReturn(0L); // Pretend we are an activity that is starting up FragmentActivity temp = Robolectric.setupActivity(FragmentActivity.class); when(mFragment.getActivity()).thenReturn(temp); SurveyMixin mixin = new SurveyMixin(mFragment, FAKE_KEY); mixin.onResume(); // Verify that a download was not attempted verify(mProvider, never()).downloadSurvey(any(), any(), any()); // Verify that we did not register a receiver verify(mProvider, never()).createAndRegisterReceiver(any()); // Verify we tried to show a survey verify(mProvider, times(1)).showSurveyIfAvailable(any(), any()); } @Test public void onResume_doesNothingWhenActivityIsNull() { // Pretend the activity died somewhere in the process when(mFragment.getActivity()).thenReturn(null); SurveyMixin mixin = new SurveyMixin(mFragment, FAKE_KEY); mixin.onResume(); // Verify we don't try showing or downloading a survey verify(mProvider, never()).showSurveyIfAvailable(any(), any()); verify(mProvider, never()).downloadSurvey(any(), any(), any()); } @Test public void onPause_removesReceiverIfPreviouslySet() { // Pretend there is a survey in memory when(mProvider.getSurveyExpirationDate(any(), any())).thenReturn(-1L); // Pretend we are an activity that starts and stops FragmentActivity temp = Robolectric.setupActivity(FragmentActivity.class); when(mFragment.getActivity()).thenReturn(temp); when(mProvider.createAndRegisterReceiver(any())).thenReturn(mReceiver); LocalBroadcastManager manager = LocalBroadcastManager.getInstance(temp); SurveyMixin mixin = new SurveyMixin(mFragment, FAKE_KEY); mixin.onResume(); manager.registerReceiver(mReceiver, new IntentFilter()); mixin.onPause(); // Verify we remove the receiver HashMap<BroadcastReceiver, ArrayList<IntentFilter>> map = ReflectionHelpers.getField(manager, "mReceivers"); assertThat(map.containsKey(mReceiver)).isFalse(); } @Test public void onPause_doesNothingWhenActivityOrReceiverNull() { // Pretend there is a survey in memory when(mProvider.getSurveyExpirationDate(any(), any())).thenReturn(-1L); // Pretend we are an activity that fails to create a receiver properly FragmentActivity temp = Robolectric.setupActivity(FragmentActivity.class); when(mFragment.getActivity()).thenReturn(temp); SurveyMixin mixin = new SurveyMixin(mFragment, FAKE_KEY); mixin.onPause(); // Verify we do nothing; verify(mProvider, never()).showSurveyIfAvailable(any(), any()); // pretend the activity died before onPause when(mFragment.getActivity()).thenReturn(null); mixin.onPause(); // Verify we do nothing verify(mProvider, never()).showSurveyIfAvailable(any(), any()); verify(mProvider, times(0)).sendActivityIfAvailable(FAKE_KEY); } } Loading
src/com/android/settings/overlay/SurveyFeatureProvider.java +24 −4 Original line number Diff line number Diff line Loading @@ -26,14 +26,15 @@ import androidx.localbroadcastmanager.content.LocalBroadcastManager; * An interface for classes wishing to provide the ability to serve surveys to implement. */ public interface SurveyFeatureProvider { /** * Downloads a survey asynchronously to shared preferences to be served at a later date. * * @param activity A valid context. * @param surveyId A unique Id representing a survey to download. * @param data a text blob to be attached to the survey results. * @deprecated This is not used after T. */ @Deprecated void downloadSurvey(Activity activity, String surveyId, @Nullable String data); /** Loading @@ -42,7 +43,9 @@ public interface SurveyFeatureProvider { * @param activity The host activity to show the survey in. * @param surveyId A unique Id representing a survey to download. * @return A boolean indicating if a survey was shown or not. * @deprecated This is not used after T. */ @Deprecated boolean showSurveyIfAvailable(Activity activity, String surveyId); /** Loading @@ -52,7 +55,9 @@ public interface SurveyFeatureProvider { * @param context A valid context. * @param simpleKey The simple name of the key to get the surveyId for. * @return The unique Id as a string or null on error. * @deprecated This is not used after T. */ @Deprecated String getSurveyId(Context context, String simpleKey); /** Loading @@ -64,24 +69,32 @@ public interface SurveyFeatureProvider { * @param surveyId the site ID. * @return the unix timestamp for the available survey for the given {@coe siteId} or -1 if * there is none available. * @deprecated This is not used after T. */ @Deprecated long getSurveyExpirationDate(Context context, String surveyId); /** * Registers an activity to show surveys/prompts as soon as they are downloaded. The receiver * should be unregistered prior to destroying the activity to avoid undefined behavior by * calling {@link #unregisterReceiver(Activity, BroadcastReceiver)}. * * @param activity The activity that should show surveys once they are downloaded. * @return the broadcast receiver listening for survey downloads. Must be unregistered before * leaving the activity. * @deprecated This is not used after T. */ @Deprecated BroadcastReceiver createAndRegisterReceiver(Activity activity); /** * Unregisters the broadcast receiver for this activity. Should only be called once per activity * after a call to {@link #createAndRegisterReceiver(Activity)}. * * @param activity The activity that was used to register the BroadcastReceiver. * @deprecated This is not used after T. */ @Deprecated static void unregisterReceiver(Activity activity, BroadcastReceiver receiver) { if (activity == null) { throw new IllegalStateException("Cannot unregister receiver if activity is null"); Loading @@ -89,4 +102,11 @@ public interface SurveyFeatureProvider { LocalBroadcastManager.getInstance(activity).unregisterReceiver(receiver); } /** * Send the visited activity to the place where it will trigger a survey if possible. * * @param simpleKey The simple name of the key to get the surveyId for. */ void sendActivityIfAvailable(String simpleKey); }
src/com/android/settings/survey/SurveyMixin.java +5 −25 Original line number Diff line number Diff line Loading @@ -16,14 +16,12 @@ package com.android.settings.survey; import android.app.Activity; import android.content.BroadcastReceiver; import androidx.fragment.app.Fragment; import com.android.settings.overlay.FeatureFactory; import com.android.settings.overlay.SurveyFeatureProvider; import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.events.OnPause; import com.android.settingslib.core.lifecycle.events.OnResume; /** Loading @@ -31,16 +29,16 @@ import com.android.settingslib.core.lifecycle.events.OnResume; * in settings. This allows new classes to automatically support settings provided the extend * one of the relevant classes in com.android.settings.lifecycle. */ public class SurveyMixin implements LifecycleObserver, OnResume, OnPause { public class SurveyMixin implements LifecycleObserver, OnResume { private String mName; private Fragment mFragment; private BroadcastReceiver mReceiver; /** * A mixin that attempts to perform survey related tasks right before onResume is called * in a Settings PreferenceFragment. This will allow for remote updating and creation of * surveys. * * @param fragment The fragment that this mixin will be attached to. * @param fragmentName The simple name of the fragment. */ Loading @@ -53,31 +51,13 @@ public class SurveyMixin implements LifecycleObserver, OnResume, OnPause { public void onResume() { Activity activity = mFragment.getActivity(); // guard against the activity not existing yet or the feature being disabled // guard against the activity not existing yet if (activity != null) { SurveyFeatureProvider provider = FeatureFactory.getFactory(activity).getSurveyFeatureProvider(activity); if (provider != null) { // Try to download a survey if there is none available, show the survey otherwise String id = provider.getSurveyId(activity, mName); if (provider.getSurveyExpirationDate(activity, id) <= -1) { // register the receiver to show the survey on completion. mReceiver = provider.createAndRegisterReceiver(activity); provider.downloadSurvey(activity, id, null /* data */); } else { provider.showSurveyIfAvailable(activity, id); } } provider.sendActivityIfAvailable(mName); } } @Override public void onPause() { Activity activity = mFragment.getActivity(); if (mReceiver != null && activity != null) { SurveyFeatureProvider.unregisterReceiver(activity, mReceiver); mReceiver = null; } } }
tests/robotests/src/com/android/settings/survey/SurveyMixinTest.java +2 −100 Original line number Diff line number Diff line package com.android.settings.survey; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.BroadcastReceiver; import android.content.Context; import android.content.IntentFilter; import androidx.fragment.app.FragmentActivity; import androidx.localbroadcastmanager.content.LocalBroadcastManager; import com.android.settings.core.InstrumentedPreferenceFragment; import com.android.settings.overlay.SurveyFeatureProvider; Loading @@ -28,22 +20,15 @@ import org.mockito.MockitoAnnotations; import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.util.ReflectionHelpers; import java.util.ArrayList; import java.util.HashMap; @RunWith(RobolectricTestRunner.class) public class SurveyMixinTest { private static final String FAKE_KEY = "fake_key"; private static final String FAKE_SURVEY_ID = "fake_id"; private Context mContext; private SurveyFeatureProvider mProvider; @Mock private BroadcastReceiver mReceiver; @Mock private InstrumentedPreferenceFragment mFragment; @Before Loading @@ -52,99 +37,16 @@ public class SurveyMixinTest { MockitoAnnotations.initMocks(this); mContext = RuntimeEnvironment.application; mProvider = FakeFeatureFactory.setupForTest().getSurveyFeatureProvider(mContext); when(mProvider.getSurveyId(any(), eq(FAKE_KEY))).thenReturn(FAKE_SURVEY_ID); } @Test public void onResume_triesRegisteringReceiverAndDownloadingWhenNoSurveyDetected() { // Pretend there is no survey in memory when(mProvider.getSurveyExpirationDate(any(), any())).thenReturn(-1L); public void onResume_noActionIfActivityDoesNotExist() { // Pretend we are an activity that is starting up FragmentActivity temp = Robolectric.setupActivity(FragmentActivity.class); when(mFragment.getActivity()).thenReturn(temp); SurveyMixin mixin = new SurveyMixin(mFragment, FAKE_KEY); mixin.onResume(); // Verify that a download was attempted verify(mProvider, times(1)).downloadSurvey(any(), any(), any()); // Verify that we registered a receiver for download completion broadcasts verify(mProvider, times(1)).createAndRegisterReceiver(any()); // Verify we did not try to show a survey verify(mProvider, never()).showSurveyIfAvailable(any(), any()); } @Test public void onResume_triesShowingSurveyWhenOneIsPresent() { // Pretend there is a survey in memory when(mProvider.getSurveyExpirationDate(any(), any())).thenReturn(0L); // Pretend we are an activity that is starting up FragmentActivity temp = Robolectric.setupActivity(FragmentActivity.class); when(mFragment.getActivity()).thenReturn(temp); SurveyMixin mixin = new SurveyMixin(mFragment, FAKE_KEY); mixin.onResume(); // Verify that a download was not attempted verify(mProvider, never()).downloadSurvey(any(), any(), any()); // Verify that we did not register a receiver verify(mProvider, never()).createAndRegisterReceiver(any()); // Verify we tried to show a survey verify(mProvider, times(1)).showSurveyIfAvailable(any(), any()); } @Test public void onResume_doesNothingWhenActivityIsNull() { // Pretend the activity died somewhere in the process when(mFragment.getActivity()).thenReturn(null); SurveyMixin mixin = new SurveyMixin(mFragment, FAKE_KEY); mixin.onResume(); // Verify we don't try showing or downloading a survey verify(mProvider, never()).showSurveyIfAvailable(any(), any()); verify(mProvider, never()).downloadSurvey(any(), any(), any()); } @Test public void onPause_removesReceiverIfPreviouslySet() { // Pretend there is a survey in memory when(mProvider.getSurveyExpirationDate(any(), any())).thenReturn(-1L); // Pretend we are an activity that starts and stops FragmentActivity temp = Robolectric.setupActivity(FragmentActivity.class); when(mFragment.getActivity()).thenReturn(temp); when(mProvider.createAndRegisterReceiver(any())).thenReturn(mReceiver); LocalBroadcastManager manager = LocalBroadcastManager.getInstance(temp); SurveyMixin mixin = new SurveyMixin(mFragment, FAKE_KEY); mixin.onResume(); manager.registerReceiver(mReceiver, new IntentFilter()); mixin.onPause(); // Verify we remove the receiver HashMap<BroadcastReceiver, ArrayList<IntentFilter>> map = ReflectionHelpers.getField(manager, "mReceivers"); assertThat(map.containsKey(mReceiver)).isFalse(); } @Test public void onPause_doesNothingWhenActivityOrReceiverNull() { // Pretend there is a survey in memory when(mProvider.getSurveyExpirationDate(any(), any())).thenReturn(-1L); // Pretend we are an activity that fails to create a receiver properly FragmentActivity temp = Robolectric.setupActivity(FragmentActivity.class); when(mFragment.getActivity()).thenReturn(temp); SurveyMixin mixin = new SurveyMixin(mFragment, FAKE_KEY); mixin.onPause(); // Verify we do nothing; verify(mProvider, never()).showSurveyIfAvailable(any(), any()); // pretend the activity died before onPause when(mFragment.getActivity()).thenReturn(null); mixin.onPause(); // Verify we do nothing verify(mProvider, never()).showSurveyIfAvailable(any(), any()); verify(mProvider, times(0)).sendActivityIfAvailable(FAKE_KEY); } }