Files
packages_apps_Settings/src/com/android/settings/homepage/contextualcards/ContextualCardManager.java
Emily Chuang f519e3ffe5 Make ContextualCardLoader do entire loading upon fragment's onStart()
For dismissal mechanism, we will have to get information about those
dismissed cards that are stored in Card DB with flag "dismiss" to filter
them out. Currently we only do the entire loading in fragment's
onCreate(), so only when the fragment is recreated users can get the new
data. Now we are changing it to onStart() and make the loader restart so
we are able to show the latest cards.

Change-Id: I4c0be297232f026b46feb8084084816e1acc8f11
Fixes: 119090460
Bug: 113783548
Test: visual
2018-11-06 19:56:42 +08:00

194 lines
7.3 KiB
Java

/*
* 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 com.android.settings.homepage.contextualcards.ContextualCardLoader.CARD_CONTENT_LOADER_ID;
import static java.util.stream.Collectors.groupingBy;
import android.content.Context;
import android.os.Bundle;
import android.util.ArrayMap;
import android.util.Log;
import android.widget.BaseAdapter;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
* This is a centralized manager of multiple {@link ContextualCardController}.
*
* {@link ContextualCardManager} first loads data from {@link ContextualCardLoader} and gets back a
* list of {@link ContextualCard}. All subclasses of {@link ContextualCardController} are loaded
* here, which will then trigger the {@link ContextualCardController} to load its data and listen to
* corresponding changes. When every single {@link ContextualCardController} updates its data, the
* data will be passed here, then going through some sorting mechanisms. The
* {@link ContextualCardController} will end up building a list of {@link ContextualCard} for
* {@link ContextualCardsAdapter} and {@link BaseAdapter#notifyDataSetChanged()} will be called to
* get the page refreshed.
*/
public class ContextualCardManager implements ContextualCardLoader.CardContentLoaderListener,
ContextualCardUpdateListener {
private static final String TAG = "ContextualCardManager";
//The list for Settings Custom Card
@ContextualCard.CardType
private static final int[] SETTINGS_CARDS = {ContextualCard.CardType.CONDITIONAL};
private final Context mContext;
private final ControllerRendererPool mControllerRendererPool;
private final Lifecycle mLifecycle;
private final List<ContextualCard> mContextualCards;
private final List<LifecycleObserver> mLifecycleObservers;
private ContextualCardUpdateListener mListener;
public ContextualCardManager(Context context, @NonNull 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) {
setupController(cardType);
}
}
void loadContextualCards(ContextualCardsFragment fragment) {
final CardContentLoaderCallbacks cardContentLoaderCallbacks =
new CardContentLoaderCallbacks(mContext);
cardContentLoaderCallbacks.setListener(this);
LoaderManager.getInstance(fragment).restartLoader(CARD_CONTENT_LOADER_ID, null /* bundle */,
cardContentLoaderCallbacks);
}
private void loadCardControllers() {
for (ContextualCard card : mContextualCards) {
setupController(card.getCardType());
}
}
private void setupController(int cardType) {
final ContextualCardController controller = mControllerRendererPool.getController(mContext,
cardType);
if (controller == null) {
Log.w(TAG, "Cannot find ContextualCardController for type " + cardType);
return;
}
controller.setCardUpdateListener(this);
if (controller instanceof LifecycleObserver && !mLifecycleObservers.contains(controller)) {
mLifecycleObservers.add((LifecycleObserver) controller);
mLifecycle.addObserver((LifecycleObserver) controller);
}
}
//TODO(b/111822376): implement sorting mechanism.
private void sortCards(List<ContextualCard> cards) {
//take mContextualCards as the source and do the ranking based on the rule.
}
@Override
public void onContextualCardUpdated(Map<Integer, List<ContextualCard>> updateList) {
final Set<Integer> cardTypes = updateList.keySet();
//Remove the existing data that matches the certain cardType before inserting new data.
final List<ContextualCard> cardsToKeep = mContextualCards
.stream()
.filter(card -> !cardTypes.contains(card.getCardType()))
.collect(Collectors.toList());
final List<ContextualCard> allCards = new ArrayList<>();
allCards.addAll(cardsToKeep);
allCards.addAll(
updateList.values().stream().flatMap(List::stream).collect(Collectors.toList()));
sortCards(allCards);
//replace with the new data
mContextualCards.clear();
mContextualCards.addAll(allCards);
loadCardControllers();
if (mListener != null) {
final Map<Integer, List<ContextualCard>> cardsToUpdate = new ArrayMap<>();
cardsToUpdate.put(ContextualCard.CardType.DEFAULT, mContextualCards);
mListener.onContextualCardUpdated(cardsToUpdate);
}
}
@Override
public void onFinishCardLoading(List<ContextualCard> cards) {
onContextualCardUpdated(cards.stream().collect(groupingBy(ContextualCard::getCardType)));
}
void setListener(ContextualCardUpdateListener listener) {
mListener = listener;
}
public ControllerRendererPool getControllerRendererPool() {
return mControllerRendererPool;
}
static class CardContentLoaderCallbacks implements
LoaderManager.LoaderCallbacks<List<ContextualCard>> {
private Context mContext;
private ContextualCardLoader.CardContentLoaderListener mListener;
CardContentLoaderCallbacks(Context context) {
mContext = context.getApplicationContext();
}
protected void setListener(ContextualCardLoader.CardContentLoaderListener listener) {
mListener = listener;
}
@NonNull
@Override
public Loader<List<ContextualCard>> onCreateLoader(int id, @Nullable Bundle bundle) {
if (id == CARD_CONTENT_LOADER_ID) {
return new ContextualCardLoader(mContext);
} else {
throw new IllegalArgumentException("Unknown loader id: " + id);
}
}
@Override
public void onLoadFinished(@NonNull Loader<List<ContextualCard>> loader,
List<ContextualCard> contextualCards) {
if (mListener != null) {
mListener.onFinishCardLoading(contextualCards);
}
}
@Override
public void onLoaderReset(@NonNull Loader<List<ContextualCard>> loader) {
}
}
}