Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 34912fb2 authored by Binyi Wu's avatar Binyi Wu Committed by Android (Google) Code Review
Browse files

Merge "Update survey triggering mechanisms"

parents 501d80af d43ecab8
Loading
Loading
Loading
Loading
+24 −4
Original line number Diff line number Diff line
@@ -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);

    /**
@@ -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);

    /**
@@ -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);

    /**
@@ -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");
@@ -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);
}
+5 −25
Original line number Diff line number Diff line
@@ -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;

/**
@@ -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.
     */
@@ -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;
        }
    }
}
+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;
@@ -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
@@ -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);
    }
}