// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.ntp.snippets;
import android.graphics.Bitmap;
import org.chromium.base.Callback;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.chrome.browser.ntp.cards.SuggestionsCategoryInfo;
import org.chromium.chrome.browser.ntp.snippets.CategoryStatus.CategoryStatusEnum;
import org.chromium.chrome.browser.profiles.Profile;
import java.util.ArrayList;
import java.util.List;
/**
* Provides access to the snippets to display on the NTP using the C++ ContentSuggestionsService.
*/
public class SnippetsBridge implements SuggestionsSource {
private static final String TAG = "SnippetsBridge";
private long mNativeSnippetsBridge;
private SuggestionsSource.Observer mObserver;
public static boolean isCategoryStatusAvailable(@CategoryStatusEnum int status) {
// Note: This code is duplicated in content_suggestions_category_status.cc.
return status == CategoryStatus.AVAILABLE_LOADING || status == CategoryStatus.AVAILABLE;
}
/** Returns whether the category is considered "enabled", and can show content suggestions. */
public static boolean isCategoryEnabled(@CategoryStatusEnum int status) {
switch (status) {
case CategoryStatus.INITIALIZING:
case CategoryStatus.AVAILABLE:
case CategoryStatus.AVAILABLE_LOADING:
case CategoryStatus.SIGNED_OUT:
return true;
}
return false;
}
public static boolean isCategoryLoading(@CategoryStatusEnum int status) {
return status == CategoryStatus.AVAILABLE_LOADING || status == CategoryStatus.INITIALIZING;
}
/**
* Creates a SnippetsBridge for getting snippet data for the current user.
*
* @param profile Profile of the user that we will retrieve snippets for.
*/
public SnippetsBridge(Profile profile) {
mNativeSnippetsBridge = nativeInit(profile);
}
/**
* Destroys the native service and unregisters observers. This object can't be reused to
* communicate with any native service and should be discarded.
*/
public void destroy() {
assert mNativeSnippetsBridge != 0;
nativeDestroy(mNativeSnippetsBridge);
mNativeSnippetsBridge = 0;
mObserver = null;
}
/**
* Fetches new snippets.
*/
public static void fetchSnippets(boolean forceRequest) {
nativeFetchSnippets(forceRequest);
}
/**
* Reschedules the fetching of snippets.
*/
public static void rescheduleFetching() {
nativeRescheduleFetching();
}
@Override
public int[] getCategories() {
assert mNativeSnippetsBridge != 0;
return nativeGetCategories(mNativeSnippetsBridge);
}
@Override
@CategoryStatusEnum
public int getCategoryStatus(int category) {
assert mNativeSnippetsBridge != 0;
return nativeGetCategoryStatus(mNativeSnippetsBridge, category);
}
@Override
public SuggestionsCategoryInfo getCategoryInfo(int category) {
assert mNativeSnippetsBridge != 0;
return nativeGetCategoryInfo(mNativeSnippetsBridge, category);
}
@Override
public List<SnippetArticle> getSuggestionsForCategory(int category) {
assert mNativeSnippetsBridge != 0;
return nativeGetSuggestionsForCategory(mNativeSnippetsBridge, category);
}
@Override
public void fetchSuggestionImage(SnippetArticle suggestion, Callback<Bitmap> callback) {
assert mNativeSnippetsBridge != 0;
nativeFetchSuggestionImage(mNativeSnippetsBridge, suggestion.mCategory,
suggestion.mIdWithinCategory, callback);
}
@Override
public void dismissSuggestion(SnippetArticle suggestion) {
assert mNativeSnippetsBridge != 0;
nativeDismissSuggestion(mNativeSnippetsBridge, suggestion.mUrl, suggestion.mGlobalPosition,
suggestion.mCategory, suggestion.mPosition, suggestion.mIdWithinCategory);
}
@Override
public void dismissCategory(@CategoryInt int category) {
assert mNativeSnippetsBridge != 0;
nativeDismissCategory(mNativeSnippetsBridge, category);
}
@Override
public void getSuggestionVisited(SnippetArticle suggestion, Callback<Boolean> callback) {
assert mNativeSnippetsBridge != 0;
nativeGetURLVisited(mNativeSnippetsBridge, callback, suggestion.mUrl);
}
public void onPageShown(int[] categories, int[] suggestionsPerCategory) {
assert mNativeSnippetsBridge != 0;
nativeOnPageShown(mNativeSnippetsBridge, categories, suggestionsPerCategory);
}
public void onSuggestionShown(SnippetArticle suggestion) {
assert mNativeSnippetsBridge != 0;
nativeOnSuggestionShown(mNativeSnippetsBridge, suggestion.mGlobalPosition,
suggestion.mCategory, suggestion.mPosition,
suggestion.mPublishTimestampMilliseconds, suggestion.mScore);
}
public void onSuggestionOpened(SnippetArticle suggestion, int windowOpenDisposition) {
assert mNativeSnippetsBridge != 0;
nativeOnSuggestionOpened(mNativeSnippetsBridge, suggestion.mGlobalPosition,
suggestion.mCategory, suggestion.mPosition,
suggestion.mPublishTimestampMilliseconds, suggestion.mScore, windowOpenDisposition);
}
public void onSuggestionMenuOpened(SnippetArticle suggestion) {
assert mNativeSnippetsBridge != 0;
nativeOnSuggestionMenuOpened(mNativeSnippetsBridge, suggestion.mGlobalPosition,
suggestion.mCategory, suggestion.mPosition,
suggestion.mPublishTimestampMilliseconds, suggestion.mScore);
}
public void onMoreButtonShown(int category, int position) {
assert mNativeSnippetsBridge != 0;
nativeOnMoreButtonShown(mNativeSnippetsBridge, category, position);
}
public void onMoreButtonClicked(int category, int position) {
assert mNativeSnippetsBridge != 0;
nativeOnMoreButtonClicked(mNativeSnippetsBridge, category, position);
}
public static void onSuggestionTargetVisited(int category, long visitTimeMs) {
nativeOnSuggestionTargetVisited(category, visitTimeMs);
}
/**
* Sets the recipient for the fetched snippets.
*
* An observer needs to be set before the native code attempts to transmit snippets them to
* java. Upon registration, the observer will be notified of already fetched snippets.
*
* @param observer object to notify when snippets are received, or {@code null} if we want to
* stop observing.
*/
@Override
public void setObserver(SuggestionsSource.Observer observer) {
assert mObserver == null || mObserver == observer;
mObserver = observer;
nativeSetObserver(mNativeSnippetsBridge, observer == null ? null : this);
}
@CalledByNative
private static List<SnippetArticle> createSuggestionList() {
return new ArrayList<>();
}
@CalledByNative
private static void addSuggestion(List<SnippetArticle> suggestions, int category, String id,
String title, String publisher, String previewText, String url, String ampUrl,
long timestamp, float score, int cardLayout) {
int position = suggestions.size();
suggestions.add(new SnippetArticle(category, id, title, publisher, previewText, url, ampUrl,
timestamp, score, position, cardLayout));
}
@CalledByNative
private static SuggestionsCategoryInfo createSuggestionsCategoryInfo(int category, String title,
int cardLayout, boolean hasMoreButton, boolean showIfEmpty) {
return new SuggestionsCategoryInfo(category, title, cardLayout, hasMoreButton, showIfEmpty);
}
@CalledByNative
private void onNewSuggestions(@CategoryInt int category) {
assert mNativeSnippetsBridge != 0;
assert mObserver != null;
mObserver.onNewSuggestions(category);
}
@CalledByNative
private void onCategoryStatusChanged(@CategoryInt int category,
@CategoryStatusEnum int newStatus) {
if (mObserver != null) mObserver.onCategoryStatusChanged(category, newStatus);
}
@CalledByNative
private void onSuggestionInvalidated(@CategoryInt int category, String idWithinCategory) {
if (mObserver != null) mObserver.onSuggestionInvalidated(category, idWithinCategory);
}
private native long nativeInit(Profile profile);
private native void nativeDestroy(long nativeNTPSnippetsBridge);
private static native void nativeFetchSnippets(boolean forceRequest);
private static native void nativeRescheduleFetching();
private native int[] nativeGetCategories(long nativeNTPSnippetsBridge);
private native int nativeGetCategoryStatus(long nativeNTPSnippetsBridge, int category);
private native SuggestionsCategoryInfo nativeGetCategoryInfo(
long nativeNTPSnippetsBridge, int category);
private native List<SnippetArticle> nativeGetSuggestionsForCategory(
long nativeNTPSnippetsBridge, int category);
private native void nativeFetchSuggestionImage(long nativeNTPSnippetsBridge, int category,
String idWithinCategory, Callback<Bitmap> callback);
private native void nativeDismissSuggestion(long nativeNTPSnippetsBridge, String url,
int globalPosition, int category, int categoryPosition, String idWithinCategory);
private native void nativeDismissCategory(long nativeNTPSnippetsBridge, int category);
private native void nativeGetURLVisited(
long nativeNTPSnippetsBridge, Callback<Boolean> callback, String url);
private native void nativeOnPageShown(
long nativeNTPSnippetsBridge, int[] categories, int[] suggestionsPerCategory);
private native void nativeOnSuggestionShown(long nativeNTPSnippetsBridge, int globalPosition,
int category, int categoryPosition, long publishTimestampMs, float score);
private native void nativeOnSuggestionOpened(long nativeNTPSnippetsBridge, int globalPosition,
int category, int categoryPosition, long publishTimestampMs, float score,
int windowOpenDisposition);
private native void nativeOnSuggestionMenuOpened(long nativeNTPSnippetsBridge,
int globalPosition, int category, int categoryPosition, long publishTimestampMs,
float score);
private native void nativeOnMoreButtonShown(
long nativeNTPSnippetsBridge, int category, int position);
private native void nativeOnMoreButtonClicked(
long nativeNTPSnippetsBridge, int category, int position);
private static native void nativeOnSuggestionTargetVisited(int category, long visitTimeMs);
private native void nativeSetObserver(long nativeNTPSnippetsBridge, SnippetsBridge bridge);
}