Skip to content

Commit

Permalink
Rich NTT on Android
Browse files Browse the repository at this point in the history
  • Loading branch information
aseren committed Feb 18, 2025
1 parent bf80607 commit d0e6799
Show file tree
Hide file tree
Showing 23 changed files with 513 additions and 7 deletions.
1 change: 1 addition & 0 deletions android/brave_java_sources.gni
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ brave_java_sources = [
"../../brave/android/java/org/chromium/chrome/browser/ntp_background_images/model/ImageCredit.java",
"../../brave/android/java/org/chromium/chrome/browser/ntp_background_images/model/NTPImage.java",
"../../brave/android/java/org/chromium/chrome/browser/ntp_background_images/model/SponsoredLogo.java",
"../../brave/android/java/org/chromium/chrome/browser/ntp_background_images/model/SponsoredRichMedia.java",
"../../brave/android/java/org/chromium/chrome/browser/ntp_background_images/model/SponsoredTab.java",
"../../brave/android/java/org/chromium/chrome/browser/ntp_background_images/model/TopSite.java",
"../../brave/android/java/org/chromium/chrome/browser/ntp_background_images/model/Wallpaper.java",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,22 @@
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArrayList;

import org.chromium.content_public.browser.WebContents;
import android.widget.FrameLayout;
import org.chromium.components.thinwebview.ThinWebView;
import org.chromium.components.thinwebview.ThinWebViewConstraints;
import org.chromium.components.thinwebview.ThinWebViewFactory;
import org.chromium.components.embedder_support.view.ContentView;
import org.chromium.ui.base.IntentRequestTracker;
import org.chromium.content_public.browser.LoadUrlParams;
import org.chromium.chrome.browser.content.WebContentsFactory;
import org.chromium.ui.base.ViewAndroidDelegate;
import org.chromium.base.version_info.VersionInfo;
import androidx.recyclerview.widget.RecyclerView.OnItemTouchListener;
import org.chromium.chrome.browser.ntp_background_images.model.SponsoredRichMedia;
import org.chromium.net.NetId;
import android.net.Uri;

public class BraveNewTabPageLayout
extends NewTabPageLayout implements ConnectionErrorHandler, OnBraveNtpListener {
private static final String TAG = "BraveNewTabPage";
Expand All @@ -132,7 +148,14 @@ public class BraveNewTabPageLayout
private Integer mInitialTileNum;

// Own members.
private WindowAndroid mWindowAndroid;

private ImageView mBgImageView;
private WebContents mSponsoredBackgroundWebContents;
private ThinWebView mSponsoredBackgroundWebView;
private FrameLayout mBackgroundSponsoredContentView;
private SponsoredRichMedia mSponsoredRichMedia;

private Profile mProfile;
private SponsoredTab mSponsoredTab;
private boolean mIsTablet;
Expand Down Expand Up @@ -441,6 +464,34 @@ private void setNtpRecyclerView(LinearLayoutManager linearLayoutManager) {
}

mPrevVisibleNewsCardPosition = firstNewsFeedPosition() - 1;

mRecyclerView.addOnItemTouchListener(
new OnItemTouchListener() {
@Override
public boolean onInterceptTouchEvent(
RecyclerView recyclerView, MotionEvent event) {
final View childView =
recyclerView.findChildViewUnder(event.getX(), event.getY());
final int action = event.getActionMasked();
if (childView == null &&
(action == MotionEvent.ACTION_MOVE ||
action == MotionEvent.ACTION_DOWN)) {
mSponsoredBackgroundWebView.getView().dispatchTouchEvent(event);
}
return false;
}

@Override
public void onTouchEvent(RecyclerView recyclerView, MotionEvent event) {
}

@Override
public void onRequestDisallowInterceptTouchEvent(
boolean disallowIntercept) {
}
}
);

mRecyclerView.addOnScrollListener(
new RecyclerView.OnScrollListener() {
@Override
Expand Down Expand Up @@ -931,6 +982,11 @@ public void onConfigurationChanged(Configuration newConfig) {
if (mWallpaper == null) {
mSponsoredTab.setNTPImage(SponsoredImageUtil.getBackgroundImage());
}
} else if (ntpImage instanceof SponsoredRichMedia) {
mSponsoredRichMedia = (SponsoredRichMedia) ntpImage;
if (mSponsoredRichMedia == null) {
mSponsoredTab.setNTPImage(SponsoredImageUtil.getBackgroundImage());
}
}
super.onConfigurationChanged(newConfig);
showNTPImage(ntpImage);
Expand Down Expand Up @@ -1181,6 +1237,8 @@ public void initialize(
tabStripHeightSupplier);

mIsTablet = isTablet;
mWindowAndroid = windowAndroid;


assert mMvTilesContainerLayout != null : "Something has changed in the upstream!";

Expand All @@ -1199,6 +1257,7 @@ public void initialize(
mActivity = activity;
((BraveActivity) mActivity).dismissShieldsTooltip();
((BraveActivity) mActivity).setNewTabPageManager(manager);

}

protected boolean useFixedMVTLayout() {
Expand Down Expand Up @@ -1227,9 +1286,60 @@ private void showNTPImage(NTPImage ntpImage) {
&& mSponsoredTab != null
&& NTPImageUtil.shouldEnableNTPFeature()) {
setBackgroundImage(ntpImage);
} else if (ntpImage instanceof SponsoredRichMedia) {
final SponsoredRichMedia sponsoredRichMedia = (SponsoredRichMedia) ntpImage;
setupSponsoredBackgroundContent(sponsoredRichMedia);
}
}

private void setupSponsoredBackgroundContent(SponsoredRichMedia sponsoredRichMedia) {
mBackgroundSponsoredContentView = findViewById(R.id.bg_background_view);
mBackgroundSponsoredContentView.setVisibility(View.VISIBLE);

mSponsoredBackgroundWebContents =
WebContentsFactory.createWebContentsWithWarmRenderer(
mProfile,
/* initiallyHidden= */ true,
/* targetNetwork= */ NetId.INVALID);

final ContentView webContentView =
ContentView.createContentView(mActivity, mSponsoredBackgroundWebContents);
final ViewAndroidDelegate delegate =
ViewAndroidDelegate.createBasicDelegate(webContentView);
mSponsoredBackgroundWebContents.setDelegates(
VersionInfo.getProductVersion(),
delegate,
webContentView,
mWindowAndroid,
WebContents.createDefaultInternalsHolder());

final IntentRequestTracker intentRequestTracker = mWindowAndroid.getIntentRequestTracker();
mSponsoredBackgroundWebView =
ThinWebViewFactory.create(
mActivity, new ThinWebViewConstraints(), intentRequestTracker);
mSponsoredBackgroundWebView
.getView()
.setLayoutParams(
new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
mSponsoredBackgroundWebView.attachWebContents(mSponsoredBackgroundWebContents,
webContentView,
null);

mBackgroundSponsoredContentView.addView(mSponsoredBackgroundWebView.getView());

Uri.Builder builder = Uri.parse("chrome://sponsored-rich-media").buildUpon();
builder.appendQueryParameter("path", sponsoredRichMedia.getSponsoredRichMediaPath());
builder.appendQueryParameter("creativeInstanceId", sponsoredRichMedia.getCreativeInstanceId());
builder.appendQueryParameter("placementId", sponsoredRichMedia.getPlacementId());
builder.appendQueryParameter("targetUrl", sponsoredRichMedia.getTargetUrl());

mSponsoredBackgroundWebContents
.getNavigationController()
.loadUrl(new LoadUrlParams(builder.build().toString()));
}

private void setBackgroundImage(NTPImage ntpImage) {
mBgImageView = (ImageView) findViewById(R.id.bg_image_view);
mBgImageView.setScaleType(ImageView.ScaleType.MATRIX);
Expand Down Expand Up @@ -1261,6 +1371,11 @@ private void checkAndShowNTPImage(boolean isReset) {
if (mWallpaper == null) {
mSponsoredTab.setNTPImage(SponsoredImageUtil.getBackgroundImage());
}
} else if (ntpImage instanceof SponsoredRichMedia) {
mSponsoredRichMedia = (SponsoredRichMedia) ntpImage;
if (mSponsoredRichMedia == null) {
mSponsoredTab.setNTPImage(SponsoredImageUtil.getBackgroundImage());
}
}
showNTPImage(ntpImage);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import org.chromium.chrome.browser.ntp_background_images.model.ImageCredit;
import org.chromium.chrome.browser.ntp_background_images.model.NTPImage;
import org.chromium.chrome.browser.ntp_background_images.model.TopSite;
import org.chromium.chrome.browser.ntp_background_images.model.SponsoredRichMedia;
import org.chromium.chrome.browser.ntp_background_images.model.Wallpaper;
import org.chromium.chrome.browser.ntp_background_images.util.NewTabPageListener;
import org.chromium.chrome.browser.preferences.BravePrefServiceBridge;
Expand Down Expand Up @@ -158,6 +159,11 @@ public static Wallpaper createBrandedWallpaper(String imagePath, int focalPointX
wallpaperId);
}

@CalledByNative
public static SponsoredRichMedia createSponsoredRichMedia(String sponsoredRichMediaPath, String creativeInstanceId, String placementId, String targetUrl) {
return new SponsoredRichMedia(sponsoredRichMediaPath, creativeInstanceId, placementId, targetUrl);
}

@CalledByNative
public void onUpdated() {
for (NTPBackgroundImageServiceObserver observer : mObservers) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/* Copyright (c) 2025 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/. */

package org.chromium.chrome.browser.ntp_background_images.model;

public class SponsoredRichMedia extends NTPImage {
private String mSponsoredRichMediaPath;
private String mCreativeInstanceId;
private String mPlacementId;
private String mTargetUrl;

public SponsoredRichMedia(String sponsoredRichMediaPath, String creativeInstanceId, String placementId, String targetUrl) {
mSponsoredRichMediaPath = sponsoredRichMediaPath;
mCreativeInstanceId = creativeInstanceId;
mPlacementId = placementId;
mTargetUrl = targetUrl;
}

public String getSponsoredRichMediaPath() {
return mSponsoredRichMediaPath;
}

public void setSponsoredRichMediaPath(String sponsoredRichMediaPath) {
mSponsoredRichMediaPath = sponsoredRichMediaPath;
}

public String getCreativeInstanceId() {
return mCreativeInstanceId;
}

public void setCreativeInstanceId(String creativeInstanceId) {
mCreativeInstanceId = creativeInstanceId;
}

public String getPlacementId() {
return mPlacementId;
}

public void setPlacementId(String placementId) {
mPlacementId = placementId;
}

public String getTargetUrl() {
return mTargetUrl;
}

public void setTargetUrl(String targetUrl) {
mTargetUrl = targetUrl;
}
}
6 changes: 6 additions & 0 deletions android/java/res/layout/new_tab_page_layout.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@
android:layout_height="match_parent"
android:contentDescription="@null"/>

<FrameLayout
android:id="@+id/bg_background_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
</FrameLayout>

<FrameLayout
android:id="@+id/logo_holder"
android:layout_width="match_parent"
Expand Down
4 changes: 4 additions & 0 deletions browser/brave_content_browser_client.cc
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
#include "brave/browser/ui/webui/ai_chat/ai_chat_untrusted_conversation_ui.h"
#include "brave/browser/ui/webui/brave_rewards/rewards_page_ui.h"
#include "brave/browser/ui/webui/skus_internals_ui.h"
#include "brave/browser/ui/webui/sponsored_new_tab_page/sponsored_new_tab_page_ui.h"
#include "brave/browser/url_sanitizer/url_sanitizer_service_factory.h"
#include "brave/components/ai_chat/content/browser/ai_chat_brave_search_throttle.h"
#include "brave/components/ai_chat/content/browser/ai_chat_tab_helper.h"
Expand Down Expand Up @@ -651,6 +652,9 @@ void BraveContentBrowserClient::RegisterWebUIInterfaceBrokers(

registry.ForWebUI<AdsInternalsUI>().Add<bat_ads::mojom::AdsInternals>();

registry.ForWebUI<SponsoredNewTabPageUI>()
.Add<ntp_background_images::mojom::SponsoredRichMediaAdEventHandler>();

if (base::FeatureList::IsEnabled(skus::features::kSkusFeature)) {
registry.ForWebUI<SkusInternalsUI>().Add<skus::mojom::SkusInternals>();
}
Expand Down
42 changes: 38 additions & 4 deletions browser/ntp_background/android/ntp_background_images_bridge.cc
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,34 @@ NTPBackgroundImagesBridge::CreateBrandedWallpaper(
ConvertUTF8ToJavaString(env, wallpaper_id ? *wallpaper_id : ""));
}

base::android::ScopedJavaLocalRef<jobject>
NTPBackgroundImagesBridge::CreateSponsoredRichMedia(
const base::Value::Dict& data) {
JNIEnv* env = AttachCurrentThread();

auto* sponsored_rich_media_path =
data.FindString(ntp_background_images::kWallpaperFilePathKey);

auto* creative_instance_id =
data.FindString(ntp_background_images::kCreativeInstanceIDKey);

auto* placement_id = data.FindString(ntp_background_images::kWallpaperIDKey);

auto* target_url =
data.FindString(ntp_background_images::kLogoDestinationURLPath);

if (!sponsored_rich_media_path || !creative_instance_id || !placement_id ||
!target_url) {
return base::android::ScopedJavaLocalRef<jobject>();
}

return Java_NTPBackgroundImagesBridge_createSponsoredRichMedia(
env, ConvertUTF8ToJavaString(env, *sponsored_rich_media_path),
ConvertUTF8ToJavaString(env, *creative_instance_id),
ConvertUTF8ToJavaString(env, *placement_id),
ConvertUTF8ToJavaString(env, *target_url));
}

void NTPBackgroundImagesBridge::GetTopSites(JNIEnv* env,
const JavaParamRef<jobject>& obj) {
std::vector<ntp_background_images::TopSite> top_sites =
Expand Down Expand Up @@ -257,12 +285,18 @@ NTPBackgroundImagesBridge::GetCurrentWallpaper(
if (!data)
return base::android::ScopedJavaLocalRef<jobject>();

bool is_background =
// TODO(aseren): Add SponsoredRichMedia support.
const bool is_background =
data->FindBool(ntp_background_images::kIsBackgroundKey).value_or(false);
if (!is_background) {
return CreateBrandedWallpaper(*data);
} else {
const std::string* sponsored_rich_media_type =
data->FindString(ntp_background_images::kWallpaperTypeKey);
if (is_background) {
return CreateWallpaper(*data);
} else if (sponsored_rich_media_type &&
*sponsored_rich_media_type == "richMedia") {
return CreateSponsoredRichMedia(*data);
} else {
return CreateBrandedWallpaper(*data);
}
}

Expand Down
2 changes: 2 additions & 0 deletions browser/ntp_background/android/ntp_background_images_bridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ class NTPBackgroundImagesBridge : public NTPBackgroundImagesService::Observer,
const base::Value::Dict& data);
base::android::ScopedJavaLocalRef<jobject> CreateBrandedWallpaper(
const base::Value::Dict& data);
base::android::ScopedJavaLocalRef<jobject> CreateSponsoredRichMedia(
const base::Value::Dict& data);

raw_ptr<Profile> profile_ = nullptr;
raw_ptr<ViewCounterService> view_counter_service_ = nullptr;
Expand Down
1 change: 1 addition & 0 deletions browser/sources.gni
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ brave_chrome_browser_deps = [
"//brave/browser/themes",
"//brave/browser/ui",
"//brave/browser/ui/webui/ads_internals",
"//brave/browser/ui/webui/sponsored_new_tab_page",
"//brave/common",
"//brave/common:mojo_bindings",
"//brave/components/ai_chat/content/browser",
Expand Down
15 changes: 13 additions & 2 deletions browser/ui/webui/brave_web_ui_controller_factory.cc
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "brave/browser/ui/webui/brave_rewards_internals_ui.h"
#include "brave/browser/ui/webui/brave_rewards_page_ui.h"
#include "brave/browser/ui/webui/skus_internals_ui.h"
#include "brave/browser/ui/webui/sponsored_new_tab_page/sponsored_new_tab_page_ui.h"
#include "brave/components/ai_rewriter/common/buildflags/buildflags.h"
#include "brave/components/brave_federated/features.h"
#include "brave/components/brave_rewards/core/features.h"
Expand Down Expand Up @@ -94,7 +95,16 @@ WebUIController* NewWebUI(WebUI* web_ui, const GURL& url) {
auto host = url.host_piece();
Profile* profile = Profile::FromBrowserContext(
web_ui->GetWebContents()->GetBrowserContext());
if (host == kAdsInternalsHost) {

if (host == kSponsoredNewTabPageHost) {
return new SponsoredNewTabPageUI(
web_ui, url.host(),
brave_ads::AdsServiceFactory::GetForProfile(profile),
g_browser_process->local_state(),
g_brave_browser_process->p3a_service(),
g_brave_browser_process->ntp_background_images_service(),
profile->GetPrefs());
} else if (host == kAdsInternalsHost) {
return new AdsInternalsUI(
web_ui, url.host(),
brave_ads::AdsServiceFactory::GetForProfile(profile),
Expand Down Expand Up @@ -220,7 +230,8 @@ WebUIFactoryFunction GetWebUIFactoryFunction(WebUI* web_ui,
#endif
url.host_piece() == kRewardsPageHost ||
url.host_piece() == kRewardsInternalsHost ||
url.host_piece() == kAdsInternalsHost) {
url.host_piece() == kAdsInternalsHost ||
url.host_piece() == kSponsoredNewTabPageHost) {
return &NewWebUI;
}

Expand Down
Loading

0 comments on commit d0e6799

Please sign in to comment.