Add log feature
- Add ContextualCardFeatureProvider to handle card interaction Bug: 79698338 Test: manual Change-Id: I2a76f5ccfd07072a98ee927bed7dc39731d4cb09
This commit is contained in:
@@ -151,6 +151,9 @@
|
||||
com.android.settings.intelligence
|
||||
</string>
|
||||
|
||||
<!-- Settings intelligence interaction log intent action -->
|
||||
<string name="config_settingsintelligence_log_action" translatable="false"></string>
|
||||
|
||||
<!-- Emergency app package name -->
|
||||
<string name="config_emergency_package_name" translatable="false">
|
||||
com.android.emergency
|
||||
|
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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 android.content.Context;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/** Feature provider for the contextual card feature. */
|
||||
public interface ContextualCardFeatureProvider {
|
||||
|
||||
/** Homepage displays. */
|
||||
public void logHomepageDisplay(Context context, Long latency);
|
||||
|
||||
/** When user clicks dismiss in contextual card */
|
||||
public void logContextualCardDismiss(Context context, ContextualCard card);
|
||||
|
||||
/** After ContextualCardManager decides which cards will be displayed/hidden */
|
||||
public void logContextualCardDisplay(Context context, List<ContextualCard> showedCards,
|
||||
List<ContextualCard> hiddenCards);
|
||||
|
||||
/** When user clicks toggle/title area of a contextual card. */
|
||||
public void logContextualCardClick(Context context, ContextualCard card, int row,
|
||||
int tapTarget);
|
||||
}
|
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* 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 android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.slice.widget.EventInfo;
|
||||
|
||||
import com.android.settings.R;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ContextualCardFeatureProviderImpl implements ContextualCardFeatureProvider {
|
||||
private static final String TAG = "ContextualCardFeature";
|
||||
|
||||
// Contextual card interaction logs
|
||||
// Settings Homepage shows
|
||||
private static final int CONTEXTUAL_HOME_SHOW = 38;
|
||||
|
||||
// Contextual card shows, log card name and rank
|
||||
private static final int CONTEXTUAL_CARD_SHOW = 39;
|
||||
|
||||
// Contextual card is eligible to be shown, but doesn't rank high
|
||||
// enough, log card name and score
|
||||
private static final int CONTEXTUAL_CARD_NOT_SHOW = 40;
|
||||
|
||||
// Contextual card is dismissed, log card name
|
||||
private static final int CONTEXTUAL_CARD_DISMISS = 41;
|
||||
|
||||
// Contextual card is clicked , log card name, score, tap area
|
||||
private static final int CONTEXTUAL_CARD_CLICK = 42;
|
||||
|
||||
// SettingsLogBroadcastReceiver contracts
|
||||
// contextual card name
|
||||
private static final String EXTRA_CONTEXTUALCARD_NAME = "name";
|
||||
|
||||
// contextual card score
|
||||
private static final String EXTRA_CONTEXTUALCARD_SCORE = "score";
|
||||
|
||||
// contextual card clicked row
|
||||
private static final String EXTRA_CONTEXTUALCARD_ROW = "row";
|
||||
|
||||
// contextual card tap target
|
||||
private static final String EXTRA_CONTEXTUALCARD_TAP_TARGET = "target";
|
||||
|
||||
// contextual homepage display latency
|
||||
private static final String EXTRA_LATENCY = "latency";
|
||||
|
||||
// log type
|
||||
private static final String EXTRA_CONTEXTUALCARD_ACTION_TYPE = "type";
|
||||
|
||||
|
||||
// Contextual card tap target
|
||||
private static final int TARGET_DEFAULT = 0;
|
||||
|
||||
// Click title area
|
||||
private static final int TARGET_TITLE = 1;
|
||||
|
||||
// Click toggle
|
||||
private static final int TARGET_TOGGLE = 2;
|
||||
|
||||
// Click slider
|
||||
private static final int TARGET_SLIDER = 3;
|
||||
|
||||
@Override
|
||||
public void logHomepageDisplay(Context context, Long latency) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logContextualCardDismiss(Context context, ContextualCard card) {
|
||||
final Intent intent = new Intent();
|
||||
intent.putExtra(EXTRA_CONTEXTUALCARD_ACTION_TYPE, CONTEXTUAL_CARD_DISMISS);
|
||||
intent.putExtra(EXTRA_CONTEXTUALCARD_NAME, card.getName());
|
||||
intent.putExtra(EXTRA_CONTEXTUALCARD_SCORE, card.getRankingScore());
|
||||
sendBroadcast(context, intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logContextualCardDisplay(Context context, List<ContextualCard> showCards,
|
||||
List<ContextualCard> hiddenCards) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logContextualCardClick(Context context, ContextualCard card, int row,
|
||||
int actionType) {
|
||||
final Intent intent = new Intent();
|
||||
intent.putExtra(EXTRA_CONTEXTUALCARD_ACTION_TYPE, CONTEXTUAL_CARD_CLICK);
|
||||
intent.putExtra(EXTRA_CONTEXTUALCARD_NAME, card.getName());
|
||||
intent.putExtra(EXTRA_CONTEXTUALCARD_SCORE, card.getRankingScore());
|
||||
intent.putExtra(EXTRA_CONTEXTUALCARD_ROW, row);
|
||||
intent.putExtra(EXTRA_CONTEXTUALCARD_TAP_TARGET, actionTypeToTapTarget(actionType));
|
||||
sendBroadcast(context, intent);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void sendBroadcast(final Context context, final Intent intent) {
|
||||
intent.setPackage(context.getString(R.string.config_settingsintelligence_package_name));
|
||||
final String action = context.getString(R.string.config_settingsintelligence_log_action);
|
||||
if (!TextUtils.isEmpty(action)) {
|
||||
intent.setAction(action);
|
||||
context.sendBroadcast(intent);
|
||||
}
|
||||
}
|
||||
|
||||
private int actionTypeToTapTarget(int actionType) {
|
||||
switch (actionType) {
|
||||
case EventInfo.ACTION_TYPE_CONTENT:
|
||||
return TARGET_TITLE;
|
||||
case EventInfo.ACTION_TYPE_TOGGLE:
|
||||
return TARGET_TOGGLE;
|
||||
case EventInfo.ACTION_TYPE_SLIDER:
|
||||
return TARGET_SLIDER;
|
||||
default:
|
||||
Log.w(TAG, "unknown type " + actionType);
|
||||
return TARGET_DEFAULT;
|
||||
}
|
||||
}
|
||||
}
|
@@ -26,8 +26,10 @@ import com.android.settings.R;
|
||||
import com.android.settings.homepage.contextualcards.CardDatabaseHelper;
|
||||
import com.android.settings.homepage.contextualcards.ContextualCard;
|
||||
import com.android.settings.homepage.contextualcards.ContextualCardController;
|
||||
import com.android.settings.homepage.contextualcards.ContextualCardFeatureProvider;
|
||||
import com.android.settings.homepage.contextualcards.ContextualCardFeedbackDialog;
|
||||
import com.android.settings.homepage.contextualcards.ContextualCardUpdateListener;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
import com.android.settingslib.utils.ThreadUtils;
|
||||
|
||||
/**
|
||||
@@ -67,6 +69,9 @@ public class SliceContextualCardController implements ContextualCardController {
|
||||
dbHelper.markContextualCardAsDismissed(mContext, card.getName());
|
||||
});
|
||||
showFeedbackDialog(card);
|
||||
final ContextualCardFeatureProvider contexualCardFeatureProvider =
|
||||
FeatureFactory.getFactory(mContext).getContextualCardFeatureProvider();
|
||||
contexualCardFeatureProvider.logContextualCardDismiss(mContext, card);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -20,6 +20,7 @@ import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
@@ -38,10 +39,13 @@ import androidx.slice.widget.SliceView;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.homepage.contextualcards.ContextualCard;
|
||||
import com.android.settings.homepage.contextualcards.ContextualCardFeatureProvider;
|
||||
import com.android.settings.homepage.contextualcards.ContextualCardRenderer;
|
||||
import com.android.settings.homepage.contextualcards.ControllerRendererPool;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Card renderer for {@link ContextualCard} built as slices.
|
||||
@@ -58,6 +62,7 @@ public class SliceContextualCardRenderer implements ContextualCardRenderer,
|
||||
private final Context mContext;
|
||||
private final LifecycleOwner mLifecycleOwner;
|
||||
private final ControllerRendererPool mControllerRendererPool;
|
||||
private final Set<ContextualCard> mCardSet;
|
||||
|
||||
public SliceContextualCardRenderer(Context context, LifecycleOwner lifecycleOwner,
|
||||
ControllerRendererPool controllerRendererPool) {
|
||||
@@ -65,6 +70,7 @@ public class SliceContextualCardRenderer implements ContextualCardRenderer,
|
||||
mLifecycleOwner = lifecycleOwner;
|
||||
mSliceLiveDataMap = new ArrayMap<>();
|
||||
mControllerRendererPool = controllerRendererPool;
|
||||
mCardSet = new ArraySet<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -99,6 +105,7 @@ public class SliceContextualCardRenderer implements ContextualCardRenderer,
|
||||
sliceLiveData = SliceLiveData.fromUri(mContext, uri);
|
||||
mSliceLiveDataMap.put(uri.toString(), sliceLiveData);
|
||||
}
|
||||
mCardSet.add(card);
|
||||
|
||||
sliceLiveData.removeObservers(mLifecycleOwner);
|
||||
sliceLiveData.observe(mLifecycleOwner, slice -> {
|
||||
@@ -128,14 +135,27 @@ public class SliceContextualCardRenderer implements ContextualCardRenderer,
|
||||
|
||||
final Button btnRemove = cardHolder.itemView.findViewById(R.id.remove);
|
||||
btnRemove.setOnClickListener(v -> {
|
||||
mControllerRendererPool.getController(mContext, card.getCardType()).onDismissed(
|
||||
card);
|
||||
mControllerRendererPool.getController(mContext, card.getCardType()).onDismissed(card);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSliceAction(@NonNull EventInfo eventInfo, @NonNull SliceItem sliceItem) {
|
||||
//TODO(b/79698338): Log user interaction
|
||||
|
||||
// sliceItem.getSlice().getUri() is like
|
||||
// content://android.settings.slices/action/wifi/_gen/0/_gen/0
|
||||
// contextualCard.getSliceUri() is prefix of sliceItem.getSlice().getUri()
|
||||
for (ContextualCard card : mCardSet) {
|
||||
if (sliceItem.getSlice().getUri().toString().startsWith(
|
||||
card.getSliceUri().toString())) {
|
||||
ContextualCardFeatureProvider contexualCardFeatureProvider =
|
||||
FeatureFactory.getFactory(mContext).getContextualCardFeatureProvider();
|
||||
contexualCardFeatureProvider.logContextualCardClick(mContext, card,
|
||||
eventInfo.rowIndex, eventInfo.actionType);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class SliceViewHolder extends RecyclerView.ViewHolder {
|
||||
|
@@ -28,6 +28,7 @@ import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider;
|
||||
import com.android.settings.enterprise.EnterprisePrivacyFeatureProvider;
|
||||
import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
|
||||
import com.android.settings.gestures.AssistGestureFeatureProvider;
|
||||
import com.android.settings.homepage.contextualcards.ContextualCardFeatureProvider;
|
||||
import com.android.settings.localepicker.LocaleFeatureProvider;
|
||||
import com.android.settings.panel.PanelFeatureProvider;
|
||||
import com.android.settings.search.SearchFeatureProvider;
|
||||
@@ -108,6 +109,8 @@ public abstract class FeatureFactory {
|
||||
|
||||
public abstract PanelFeatureProvider getPanelFeatureProvider();
|
||||
|
||||
public abstract ContextualCardFeatureProvider getContextualCardFeatureProvider();
|
||||
|
||||
public static final class FactoryNotFoundException extends RuntimeException {
|
||||
public FactoryNotFoundException(Throwable throwable) {
|
||||
super("Unable to create factory. Did you misconfigure Proguard?", throwable);
|
||||
|
@@ -40,6 +40,8 @@ import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
|
||||
import com.android.settings.fuelgauge.PowerUsageFeatureProviderImpl;
|
||||
import com.android.settings.gestures.AssistGestureFeatureProvider;
|
||||
import com.android.settings.gestures.AssistGestureFeatureProviderImpl;
|
||||
import com.android.settings.homepage.contextualcards.ContextualCardFeatureProvider;
|
||||
import com.android.settings.homepage.contextualcards.ContextualCardFeatureProviderImpl;
|
||||
import com.android.settings.localepicker.LocaleFeatureProvider;
|
||||
import com.android.settings.localepicker.LocaleFeatureProviderImpl;
|
||||
import com.android.settings.panel.PanelFeatureProvider;
|
||||
@@ -75,6 +77,7 @@ public class FeatureFactoryImpl extends FeatureFactory {
|
||||
private SlicesFeatureProvider mSlicesFeatureProvider;
|
||||
private AccountFeatureProvider mAccountFeatureProvider;
|
||||
private PanelFeatureProvider mPanelFeatureProvider;
|
||||
private ContextualCardFeatureProvider mContextualCardFeatureProvider;
|
||||
|
||||
@Override
|
||||
public SupportFeatureProvider getSupportFeatureProvider(Context context) {
|
||||
@@ -220,4 +223,11 @@ public class FeatureFactoryImpl extends FeatureFactory {
|
||||
}
|
||||
return mPanelFeatureProvider;
|
||||
}
|
||||
|
||||
public ContextualCardFeatureProvider getContextualCardFeatureProvider() {
|
||||
if (mContextualCardFeatureProvider == null) {
|
||||
mContextualCardFeatureProvider = new ContextualCardFeatureProviderImpl();
|
||||
}
|
||||
return mContextualCardFeatureProvider;
|
||||
}
|
||||
}
|
||||
|
@@ -73,6 +73,11 @@
|
||||
<item>fake_package/fake_service</item>
|
||||
</string-array>
|
||||
|
||||
<!-- Settings intelligence interaction log intent action -->
|
||||
<string name="config_settingsintelligence_log_action" translatable="false">
|
||||
aaa.bbb.ccc
|
||||
</string>
|
||||
|
||||
<!-- List of packages that should be whitelisted for slice uri access. Do not translate -->
|
||||
<string-array name="slice_whitelist_package_names" translatable="false">
|
||||
<item>com.android.settings.slice_whitelist_package</item>
|
||||
|
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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 org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
@RunWith(SettingsRobolectricTestRunner.class)
|
||||
public class ContextualCardFeatureProviderImplTest {
|
||||
|
||||
private Context mContext;
|
||||
private ContextualCardFeatureProviderImpl mImpl;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mContext = spy(RuntimeEnvironment.application);
|
||||
mImpl = new ContextualCardFeatureProviderImpl();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sendBroadcast_emptyAction_notSendBroadcast() {
|
||||
final Intent intent = new Intent();
|
||||
mImpl.sendBroadcast(mContext, intent);
|
||||
|
||||
verify(mContext, never()).sendBroadcast(intent);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Config(qualifiers = "mcc999")
|
||||
public void sendBroadcast_hasAction_sendBroadcast() {
|
||||
final Intent intent = new Intent();
|
||||
mImpl.sendBroadcast(mContext, intent);
|
||||
|
||||
verify(mContext).sendBroadcast(intent);
|
||||
}
|
||||
}
|
@@ -18,9 +18,10 @@ package com.android.settings.homepage.contextualcards.slices;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
@@ -33,6 +34,7 @@ import com.android.settings.homepage.contextualcards.CardDatabaseHelper;
|
||||
import com.android.settings.homepage.contextualcards.ContextualCard;
|
||||
import com.android.settings.homepage.contextualcards.ContextualCardFeedbackDialog;
|
||||
import com.android.settings.homepage.contextualcards.ContextualCardsFragment;
|
||||
import com.android.settings.testutils.FakeFeatureFactory;
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
|
||||
import org.junit.Before;
|
||||
@@ -57,6 +59,7 @@ public class SliceContextualCardControllerTest {
|
||||
private CardContentProvider mProvider;
|
||||
private ContentResolver mResolver;
|
||||
private SliceContextualCardController mController;
|
||||
private FakeFeatureFactory mFeatureFactory;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
@@ -67,6 +70,7 @@ public class SliceContextualCardControllerTest {
|
||||
mProvider);
|
||||
mResolver = mContext.getContentResolver();
|
||||
mController = spy(new SliceContextualCardController(mContext));
|
||||
mFeatureFactory = FakeFeatureFactory.setupForTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -75,7 +79,8 @@ public class SliceContextualCardControllerTest {
|
||||
mResolver.insert(providerUri, generateOneRow());
|
||||
doNothing().when(mController).showFeedbackDialog(any(ContextualCard.class));
|
||||
|
||||
mController.onDismissed(getTestSliceCard());
|
||||
final ContextualCard card = getTestSliceCard();
|
||||
mController.onDismissed(card);
|
||||
|
||||
final String[] columns = {CardDatabaseHelper.CardColumns.CARD_DISMISSED};
|
||||
final String selection = CardDatabaseHelper.CardColumns.NAME + "=?";
|
||||
@@ -86,6 +91,8 @@ public class SliceContextualCardControllerTest {
|
||||
cr.close();
|
||||
|
||||
assertThat(qryDismissed).isEqualTo(1);
|
||||
verify(mFeatureFactory.mContextualCardFeatureProvider).logContextualCardDismiss(
|
||||
mContext, card);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@@ -28,6 +28,7 @@ import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider;
|
||||
import com.android.settings.enterprise.EnterprisePrivacyFeatureProvider;
|
||||
import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
|
||||
import com.android.settings.gestures.AssistGestureFeatureProvider;
|
||||
import com.android.settings.homepage.contextualcards.ContextualCardFeatureProvider;
|
||||
import com.android.settings.localepicker.LocaleFeatureProvider;
|
||||
import com.android.settings.overlay.DockUpdaterFeatureProvider;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
@@ -63,6 +64,7 @@ public class FakeFeatureFactory extends FeatureFactory {
|
||||
public final AssistGestureFeatureProvider assistGestureFeatureProvider;
|
||||
public final AccountFeatureProvider mAccountFeatureProvider;
|
||||
public final PanelFeatureProvider mPanelFeatureProvider;
|
||||
public final ContextualCardFeatureProvider mContextualCardFeatureProvider;
|
||||
|
||||
public SlicesFeatureProvider slicesFeatureProvider;
|
||||
public SearchFeatureProvider searchFeatureProvider;
|
||||
@@ -105,6 +107,7 @@ public class FakeFeatureFactory extends FeatureFactory {
|
||||
slicesFeatureProvider = mock(SlicesFeatureProvider.class);
|
||||
mAccountFeatureProvider = mock(AccountFeatureProvider.class);
|
||||
mPanelFeatureProvider = mock(PanelFeatureProvider.class);
|
||||
mContextualCardFeatureProvider = mock(ContextualCardFeatureProvider.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -191,4 +194,8 @@ public class FakeFeatureFactory extends FeatureFactory {
|
||||
public PanelFeatureProvider getPanelFeatureProvider() {
|
||||
return mPanelFeatureProvider;
|
||||
}
|
||||
|
||||
public ContextualCardFeatureProvider getContextualCardFeatureProvider() {
|
||||
return mContextualCardFeatureProvider;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user