Commit c796e18d authored by Jian Li's avatar Jian Li Committed by Commit Bot

Implement FeedStreamSurface.onStreamUpdated

Bug: 1044139
Change-Id: I1ecaf8f68dd047353ea746fa94f73004a5a35205
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2142952
Commit-Queue: Jian Li <jianli@chromium.org>
Reviewed-by: default avatarDan H <harringtond@chromium.org>
Cr-Commit-Position: refs/heads/master@{#758313}
parent a9a1453d
......@@ -26,20 +26,35 @@ public class FeedListContentManager implements ListContentManager {
/**
* Encapsulates the content of an item stored and managed by ListContentManager.
*/
public interface FeedContent {
public abstract static class FeedContent {
private final String mKey;
FeedContent(String key) {
assert key != null && !key.isEmpty();
mKey = key;
}
/**
* Returns true if the content is supported by the native view.
*/
boolean isNativeView();
public abstract boolean isNativeView();
/**
* Returns the key which should uniquely identify the content in the list.
*/
public String getKey() {
return mKey;
}
}
/**
* For the content that is supported by external surface controlled view.
*/
public static class ExternalViewContent implements FeedContent {
public static class ExternalViewContent extends FeedContent {
private final byte[] mData;
public ExternalViewContent(byte[] data) {
public ExternalViewContent(String key, byte[] data) {
super(key);
mData = data;
}
......@@ -60,10 +75,11 @@ public class FeedListContentManager implements ListContentManager {
/**
* For the content that is supported by the native view.
*/
public static class NativeViewContent implements FeedContent {
public static class NativeViewContent extends FeedContent {
private final View mNativeView;
public NativeViewContent(View nativeView) {
public NativeViewContent(String key, View nativeView) {
super(key);
mNativeView = nativeView;
}
......@@ -103,6 +119,21 @@ public class FeedListContentManager implements ListContentManager {
return mFeedContentList.get(index);
}
/**
* Finds the position of the content with the specified key in the list.
*
* @param key The key of the content to search for.
* @return The position if found, -1 otherwise.
*/
public int findContentPositionByKey(String key) {
for (int i = 0; i < mFeedContentList.size(); ++i) {
if (mFeedContentList.get(i).getKey().equals(key)) {
return i;
}
}
return -1;
}
/**
* Adds a list of the contents, starting at the specified position.
*
......
......@@ -4,6 +4,11 @@
package org.chromium.chrome.browser.feed.v2;
import android.view.View;
import androidx.annotation.VisibleForTesting;
import org.chromium.base.Log;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace;
import org.chromium.base.annotations.NativeMethods;
......@@ -13,11 +18,15 @@ import org.chromium.chrome.browser.tab.TabLaunchType;
import org.chromium.chrome.browser.tabmodel.TabModelSelector;
import org.chromium.chrome.browser.xsurface.FeedActionsHandler;
import org.chromium.chrome.browser.xsurface.SurfaceActionsHandler;
import org.chromium.components.feed.proto.FeedUiProto.Slice;
import org.chromium.components.feed.proto.FeedUiProto.StreamUpdate;
import org.chromium.components.feed.proto.FeedUiProto.StreamUpdate.SliceUpdate;
import org.chromium.content_public.browser.LoadUrlParams;
import org.chromium.ui.base.PageTransition;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
/**
......@@ -27,8 +36,10 @@ import java.util.List;
*/
@JNINamespace("feed")
public class FeedStreamSurface implements SurfaceActionsHandler, FeedActionsHandler {
private static final String TAG = "FeedStreamSurface";
private final long mNativeFeedStreamSurface;
private final ChromeActivity mActivity;
private final FeedListContentManager mContentManager;
/**
* Creates a {@link FeedStreamSurface} for creating native side bridge to access native feed
......@@ -38,21 +49,97 @@ public class FeedStreamSurface implements SurfaceActionsHandler, FeedActionsHand
mNativeFeedStreamSurface = FeedStreamSurfaceJni.get().init(FeedStreamSurface.this);
mActivity = activity;
FeedListContentManager manager = new FeedListContentManager(this, this);
mContentManager = new FeedListContentManager(this, this);
// TODO(jianli): Get HybridListRender in order to bind FeedListContentManager to it.
// Then add the returned RecyclerView to NTP layout.
}
@VisibleForTesting
FeedListContentManager getFeedListContentManagerForTesting() {
return mContentManager;
}
/**
* Called when the stream update content is available. The content will get passed to UI
*/
@CalledByNative
void onStreamUpdated(byte[] data) {
StreamUpdate streamUpdate;
try {
StreamUpdate streamUpdate = StreamUpdate.parseFrom(data);
streamUpdate = StreamUpdate.parseFrom(data);
} catch (com.google.protobuf.InvalidProtocolBufferException e) {
// Consume exception for now, ignoring unparsable events.
Log.wtf(TAG, "Unable to parse StreamUpdate proto data", e);
return;
}
// 1) Builds the hash map of existing content list for fast look up by slice id.
HashMap<String, FeedListContentManager.FeedContent> existingContentMap =
new HashMap<String, FeedListContentManager.FeedContent>();
for (int i = 0; i < mContentManager.getItemCount(); ++i) {
FeedListContentManager.FeedContent content = mContentManager.getContent(i);
existingContentMap.put(content.getKey(), content);
}
// 2) Builds the new list containing both new and existing contents.
ArrayList<FeedListContentManager.FeedContent> newContentList =
new ArrayList<FeedListContentManager.FeedContent>();
HashSet<String> existingIdsInNewContentList = new HashSet<String>();
for (SliceUpdate sliceUpdate : streamUpdate.getUpdatedSlicesList()) {
if (sliceUpdate.hasSlice()) {
newContentList.add(createContentFromSlice(sliceUpdate.getSlice()));
} else {
String existingSliceId = sliceUpdate.getSliceId();
FeedListContentManager.FeedContent content =
existingContentMap.get(existingSliceId);
if (content != null) {
newContentList.add(content);
existingIdsInNewContentList.add(existingSliceId);
}
}
}
// 3) Removes those contents that do not appear in the new list.
for (int i = mContentManager.getItemCount() - 1; i >= 0; --i) {
String id = mContentManager.getContent(i).getKey();
if (!existingIdsInNewContentList.contains(id)) {
mContentManager.removeContents(i, 1);
}
}
// 4) Iterates through the new list to add the new content or move the existing content
// if needed.
int i = 0;
while (i < newContentList.size()) {
FeedListContentManager.FeedContent content = newContentList.get(i);
// If this is an existing content, moves it to new position.
if (existingContentMap.containsKey(content.getKey())) {
mContentManager.moveContent(
mContentManager.findContentPositionByKey(content.getKey()), i);
++i;
continue;
}
// Otherwise, this is new content. Add it together with all adjacent new contents.
int startIndex = i++;
while (i < newContentList.size()
&& !existingContentMap.containsKey(newContentList.get(i).getKey())) {
++i;
}
mContentManager.addContents(startIndex, newContentList.subList(startIndex, i));
}
}
private FeedListContentManager.FeedContent createContentFromSlice(Slice slice) {
String sliceId = slice.getSliceId();
if (slice.hasXsurfaceSlice()) {
return new FeedListContentManager.ExternalViewContent(
sliceId, slice.getXsurfaceSlice().getXsurfaceFrame().toByteArray());
} else {
// TODO(jianli): Create native view for ZeroStateSlice.
View view = null;
return new FeedListContentManager.NativeViewContent(sliceId, view);
}
}
......
......@@ -639,6 +639,7 @@ if (enable_feed_in_chrome) {
"junit/src/org/chromium/chrome/browser/feed/NtpStreamLifecycleManagerTest.java",
"junit/src/org/chromium/chrome/browser/feed/action/FeedActionHandlerTest.java",
"junit/src/org/chromium/chrome/browser/feed/v2/FeedListContentManagerTest.java",
"junit/src/org/chromium/chrome/browser/feed/v2/FeedStreamSurfaceTest.java",
]
feed_test_java_sources = [
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Copyright 2020 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.
......@@ -46,6 +46,21 @@ public class FeedListContentManagerTest implements ListContentManagerObserver {
mManager.addObserver(this);
}
@Test
@SmallTest
public void testFindContentPositionByKey() {
FeedListContentManager.FeedContent c1 = createExternalViewContent("a");
FeedListContentManager.FeedContent c2 = createExternalViewContent("b");
FeedListContentManager.FeedContent c3 = createExternalViewContent("c");
addContents(0, ImmutableList.of(c1, c2, c3));
assertEquals(3, mManager.getItemCount());
assertEquals(0, mManager.findContentPositionByKey("a"));
assertEquals(1, mManager.findContentPositionByKey("b"));
assertEquals(2, mManager.findContentPositionByKey("c"));
}
@Test
@SmallTest
public void testAddContents() {
......@@ -215,6 +230,6 @@ public class FeedListContentManagerTest implements ListContentManagerObserver {
}
private FeedListContentManager.FeedContent createExternalViewContent(String s) {
return new FeedListContentManager.ExternalViewContent(s.getBytes());
return new FeedListContentManager.ExternalViewContent(s, s.getBytes());
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment