Loading suggestions via legacy SuggestionService sometimes

When legacy_suggestion flag is turned on, load Suggestions from
SuggestionService instead of contextual homepage, because contextual
homepage infrastructure might not be availble on all devices.

Bug: 118842099
Test: robo
Change-Id: I91710c005e11be5a9b3dd39ceff670106e7f80c3
This commit is contained in:
Fan Zhang
2018-11-05 15:40:32 -08:00
parent 16c2d70881
commit 2a98c843e8
21 changed files with 675 additions and 36 deletions

View File

@@ -33,12 +33,12 @@ public class ContextualCard {
/**
* Flags indicating the type of the ContextualCard.
*/
@IntDef({CardType.DEFAULT, CardType.SLICE, CardType.SUGGESTION, CardType.CONDITIONAL})
@IntDef({CardType.DEFAULT, CardType.SLICE, CardType.LEGACY_SUGGESTION, CardType.CONDITIONAL})
@Retention(RetentionPolicy.SOURCE)
public @interface CardType {
int DEFAULT = 0;
int SLICE = 1;
int SUGGESTION = 2;
int LEGACY_SUGGESTION = 2;
int CONDITIONAL = 3;
}

View File

@@ -18,11 +18,16 @@ package com.android.settings.homepage.contextualcards;
import android.util.Log;
import androidx.annotation.LayoutRes;
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.legacysuggestion
.LegacySuggestionContextualCardController;
import com.android.settings.homepage.contextualcards.legacysuggestion
.LegacySuggestionContextualCardRenderer;
import com.android.settings.homepage.contextualcards.slices.SliceContextualCardController;
import com.android.settings.homepage.contextualcards.slices.SliceContextualCardRenderer;
@@ -34,6 +39,7 @@ import java.util.stream.Collectors;
public class ContextualCardLookupTable {
private static final String TAG = "ContextualCardLookup";
static class ControllerRendererMapping implements Comparable<ControllerRendererMapping> {
@CardType
final int mCardType;
@@ -41,7 +47,7 @@ public class ContextualCardLookupTable {
final Class<? extends ContextualCardController> mControllerClass;
final Class<? extends ContextualCardRenderer> mRendererClass;
ControllerRendererMapping(@CardType int cardType, int viewType,
ControllerRendererMapping(@CardType int cardType, @LayoutRes int viewType,
Class<? extends ContextualCardController> controllerClass,
Class<? extends ContextualCardRenderer> rendererClass) {
mCardType = cardType;
@@ -69,6 +75,10 @@ public class ContextualCardLookupTable {
ConditionContextualCardRenderer.FULL_WIDTH_VIEW_TYPE,
ConditionContextualCardController.class,
ConditionContextualCardRenderer.class));
add(new ControllerRendererMapping(CardType.LEGACY_SUGGESTION,
LegacySuggestionContextualCardRenderer.VIEW_TYPE,
LegacySuggestionContextualCardController.class,
LegacySuggestionContextualCardRenderer.class));
add(new ControllerRendererMapping(CardType.SLICE,
SliceContextualCardRenderer.VIEW_TYPE,
SliceContextualCardController.class,

View File

@@ -16,7 +16,8 @@
package com.android.settings.homepage.contextualcards;
import static com.android.settings.homepage.contextualcards.ContextualCardLoader.CARD_CONTENT_LOADER_ID;
import static com.android.settings.homepage.contextualcards.ContextualCardLoader
.CARD_CONTENT_LOADER_ID;
import static java.util.stream.Collectors.groupingBy;
@@ -57,8 +58,8 @@ public class ContextualCardManager implements ContextualCardLoader.CardContentLo
private static final String TAG = "ContextualCardManager";
//The list for Settings Custom Card
@ContextualCard.CardType
private static final int[] SETTINGS_CARDS = {ContextualCard.CardType.CONDITIONAL};
private static final int[] SETTINGS_CARDS =
{ContextualCard.CardType.CONDITIONAL, ContextualCard.CardType.LEGACY_SUGGESTION};
private final Context mContext;
private final ControllerRendererPool mControllerRendererPool;
@@ -68,14 +69,14 @@ public class ContextualCardManager implements ContextualCardLoader.CardContentLo
private ContextualCardUpdateListener mListener;
public ContextualCardManager(Context context, @NonNull Lifecycle lifecycle) {
public ContextualCardManager(Context context, Lifecycle lifecycle) {
mContext = context;
mLifecycle = lifecycle;
mContextualCards = new ArrayList<>();
mLifecycleObservers = new ArrayList<>();
mControllerRendererPool = new ControllerRendererPool();
//for data provided by Settings
for (int cardType : SETTINGS_CARDS) {
for (@ContextualCard.CardType int cardType : SETTINGS_CARDS) {
setupController(cardType);
}
}
@@ -94,7 +95,7 @@ public class ContextualCardManager implements ContextualCardLoader.CardContentLo
}
}
private void setupController(int cardType) {
private void setupController(@ContextualCard.CardType int cardType) {
final ContextualCardController controller = mControllerRendererPool.getController(mContext,
cardType);
if (controller == null) {

View File

@@ -18,6 +18,7 @@ package com.android.settings.homepage.contextualcards;
import android.view.View;
import androidx.annotation.LayoutRes;
import androidx.recyclerview.widget.RecyclerView;
/**
@@ -28,6 +29,7 @@ public interface ContextualCardRenderer {
/**
* The layout type of the renderer.
*/
@LayoutRes
int getViewType(boolean isHalfWidth);
/**

View File

@@ -16,6 +16,8 @@
package com.android.settings.homepage.contextualcards;
import androidx.annotation.MainThread;
import java.util.List;
import java.util.Map;
@@ -31,5 +33,6 @@ public interface ContextualCardUpdateListener {
* null, which means all cards from corresponding {@link
* ContextualCard.CardType} are removed.
*/
@MainThread
void onContextualCardUpdated(Map<Integer, List<ContextualCard>> cards);
}

View File

@@ -26,6 +26,10 @@ import androidx.lifecycle.LifecycleOwner;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.homepage.contextualcards.conditional.ConditionContextualCardController;
import com.android.settings.homepage.contextualcards.conditional.ConditionContextualCardRenderer;
import com.android.settings.homepage.contextualcards.legacysuggestion
.LegacySuggestionContextualCardController;
import com.android.settings.homepage.contextualcards.legacysuggestion
.LegacySuggestionContextualCardRenderer;
import com.android.settings.homepage.contextualcards.slices.SliceContextualCardController;
import com.android.settings.homepage.contextualcards.slices.SliceContextualCardRenderer;
@@ -111,6 +115,8 @@ public class ControllerRendererPool {
return new ConditionContextualCardController(context);
} else if (SliceContextualCardController.class == clz) {
return new SliceContextualCardController();
} else if (LegacySuggestionContextualCardController.class == clz) {
return new LegacySuggestionContextualCardController(context);
}
return null;
}
@@ -118,9 +124,12 @@ public class ControllerRendererPool {
private ContextualCardRenderer createCardRenderer(Context context,
LifecycleOwner lifecycleOwner, Class<?> clz) {
if (ConditionContextualCardRenderer.class == clz) {
return new ConditionContextualCardRenderer(context, this /*controllerRendererPool*/);
return new ConditionContextualCardRenderer(context, this /* controllerRendererPool */);
} else if (SliceContextualCardRenderer.class == clz) {
return new SliceContextualCardRenderer(context, lifecycleOwner);
} else if (LegacySuggestionContextualCardRenderer.class == clz) {
return new LegacySuggestionContextualCardRenderer(context,
this /* controllerRendererPool */);
}
return null;
}

View File

@@ -23,6 +23,7 @@ import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.LayoutRes;
import androidx.recyclerview.widget.RecyclerView;
import com.android.internal.logging.nano.MetricsProto;
@@ -37,7 +38,9 @@ import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
* Card renderer for {@link ConditionalContextualCard}.
*/
public class ConditionContextualCardRenderer implements ContextualCardRenderer {
@LayoutRes
public static final int HALF_WIDTH_VIEW_TYPE = R.layout.homepage_condition_half_tile;
@LayoutRes
public static final int FULL_WIDTH_VIEW_TYPE = R.layout.homepage_condition_full_tile;
private final Context mContext;

View File

@@ -0,0 +1,61 @@
/*
* 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.legacysuggestion;
import android.app.PendingIntent;
import com.android.settings.homepage.contextualcards.ContextualCard;
public class LegacySuggestionContextualCard extends ContextualCard {
private final PendingIntent mPendingIntent;
public LegacySuggestionContextualCard(Builder builder) {
super(builder);
mPendingIntent = builder.mPendingIntent;
}
@Override
public int getCardType() {
return CardType.LEGACY_SUGGESTION;
}
public PendingIntent getPendingIntent() {
return mPendingIntent;
}
public static class Builder extends ContextualCard.Builder {
private PendingIntent mPendingIntent;
public Builder setPendingIntent(PendingIntent pendingIntent) {
mPendingIntent = pendingIntent;
return this;
}
@Override
public Builder setCardType(int cardType) {
throw new IllegalArgumentException(
"Cannot change card type for " + getClass().getName());
}
public LegacySuggestionContextualCard build() {
return new LegacySuggestionContextualCard(this);
}
}
}

View File

@@ -0,0 +1,156 @@
/*
* 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.legacysuggestion;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.service.settings.suggestions.Suggestion;
import android.util.ArrayMap;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
import com.android.settings.R;
import com.android.settings.homepage.contextualcards.ContextualCard;
import com.android.settings.homepage.contextualcards.ContextualCardController;
import com.android.settings.homepage.contextualcards.ContextualCardUpdateListener;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
import com.android.settingslib.suggestions.SuggestionController;
import com.android.settingslib.suggestions.SuggestionController.ServiceConnectionListener;
import com.android.settingslib.utils.ThreadUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class LegacySuggestionContextualCardController implements ContextualCardController,
LifecycleObserver, OnStart, OnStop, ServiceConnectionListener {
private static final String TAG = "LegacySuggestCardCtrl";
@VisibleForTesting
SuggestionController mSuggestionController;
private ContextualCardUpdateListener mCardUpdateListener;
private final Context mContext;
public LegacySuggestionContextualCardController(Context context) {
mContext = context;
if (!mContext.getResources().getBoolean(R.bool.config_use_legacy_suggestion)) {
Log.w(TAG, "Legacy suggestion contextual card disabled, skipping.");
return;
}
final ComponentName suggestionServiceComponent =
FeatureFactory.getFactory(mContext).getSuggestionFeatureProvider(mContext)
.getSuggestionServiceComponent();
mSuggestionController = new SuggestionController(
mContext, suggestionServiceComponent, this /* listener */);
}
@Override
public int getCardType() {
return ContextualCard.CardType.LEGACY_SUGGESTION;
}
@Override
public void onPrimaryClick(ContextualCard card) {
try {
((LegacySuggestionContextualCard) card).getPendingIntent().send();
} catch (PendingIntent.CanceledException e) {
Log.w(TAG, "Failed to start suggestion " + card.getTitleText());
}
}
@Override
public void onActionClick(ContextualCard card) {
}
@Override
public void setCardUpdateListener(ContextualCardUpdateListener listener) {
mCardUpdateListener = listener;
}
@Override
public void onStart() {
if (mSuggestionController == null) {
return;
}
mSuggestionController.start();
}
@Override
public void onStop() {
if (mSuggestionController == null) {
return;
}
mSuggestionController.stop();
}
@Override
public void onServiceConnected() {
loadSuggestions();
}
@Override
public void onServiceDisconnected() {
}
private void loadSuggestions() {
ThreadUtils.postOnBackgroundThread(() -> {
if (mSuggestionController == null || mCardUpdateListener == null) {
return;
}
final List<Suggestion> suggestions = mSuggestionController.getSuggestions();
Log.d(TAG, "Loaded suggests: "
+ suggestions == null ? "null" : String.valueOf(suggestions.size()));
final List<ContextualCard> cards = new ArrayList<>();
if (suggestions != null) {
// Convert suggestion to ContextualCard
for (Suggestion suggestion : suggestions) {
final LegacySuggestionContextualCard.Builder cardBuilder =
new LegacySuggestionContextualCard.Builder();
if (suggestion.getIcon() != null) {
cardBuilder.setIconDrawable(suggestion.getIcon().loadDrawable(mContext));
}
cardBuilder
.setPendingIntent(suggestion.getPendingIntent())
.setName(suggestion.getId())
.setTitleText(suggestion.getTitle().toString())
.setSummaryText(suggestion.getSummary().toString());
cards.add(cardBuilder.build());
}
}
// Update adapter
final Map<Integer, List<ContextualCard>> suggestionCards = new ArrayMap<>();
suggestionCards.put(ContextualCard.CardType.LEGACY_SUGGESTION, cards);
ThreadUtils.postOnMainThread(
() -> mCardUpdateListener.onContextualCardUpdated(suggestionCards));
});
}
}

View File

@@ -0,0 +1,81 @@
/*
* 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.legacysuggestion;
import android.content.Context;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.LayoutRes;
import androidx.recyclerview.widget.RecyclerView;
import com.android.settings.R;
import com.android.settings.homepage.contextualcards.ContextualCard;
import com.android.settings.homepage.contextualcards.ContextualCardRenderer;
import com.android.settings.homepage.contextualcards.ControllerRendererPool;
public class LegacySuggestionContextualCardRenderer implements ContextualCardRenderer {
@LayoutRes
public static final int VIEW_TYPE = R.layout.homepage_suggestion_tile;
private final Context mContext;
private final ControllerRendererPool mControllerRendererPool;
public LegacySuggestionContextualCardRenderer(Context context,
ControllerRendererPool controllerRendererPool) {
mContext = context;
mControllerRendererPool = controllerRendererPool;
}
@Override
public int getViewType(boolean isHalfWidth) {
return VIEW_TYPE;
}
@Override
public RecyclerView.ViewHolder createViewHolder(View view) {
return new LegacySuggestionViewHolder(view);
}
@Override
public void bindView(RecyclerView.ViewHolder holder, ContextualCard card) {
final LegacySuggestionViewHolder vh = (LegacySuggestionViewHolder) holder;
vh.icon.setImageDrawable(card.getIconDrawable());
vh.title.setText(card.getTitleText());
vh.summary.setText(card.getSummaryText());
vh.itemView.setOnClickListener(v ->
mControllerRendererPool.getController(mContext,
card.getCardType()).onPrimaryClick(card));
}
private static class LegacySuggestionViewHolder extends RecyclerView.ViewHolder {
public final ImageView icon;
public final TextView title;
public final TextView summary;
public LegacySuggestionViewHolder(View itemView) {
super(itemView);
icon = itemView.findViewById(android.R.id.icon);
title = itemView.findViewById(android.R.id.title);
summary = itemView.findViewById(android.R.id.summary);
}
}
}