blob: 769ade5ca4f11cd0fc9ef0e5ed5b7c1afdcabc91 [file] [log] [blame]
// Copyright 2021 The Chromium Authors
// 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.feed.feedmanagement;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.provider.Browser;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.View.OnClickListener;
import androidx.annotation.VisibleForTesting;
import androidx.browser.customtabs.CustomTabsClient;
import androidx.browser.customtabs.CustomTabsIntent;
import androidx.browser.customtabs.CustomTabsSession;
import org.chromium.base.ApiCompatibilityUtils;
import org.chromium.base.Log;
import org.chromium.base.compat.ApiHelperForM;
import org.chromium.chrome.browser.feed.FeedServiceBridge;
import org.chromium.chrome.browser.feed.R;
import org.chromium.chrome.browser.feed.StreamKind;
import org.chromium.chrome.browser.feed.v2.FeedUserActionType;
import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.ui.modelutil.MVCListAdapter.ModelList;
import org.chromium.ui.modelutil.ModelListAdapter;
import org.chromium.ui.modelutil.PropertyModel;
/**
* The MVC pattern Mediator for the Feed Management activity. This activity provides a common place
* to present management options such as managing the user's activity, their interests, and their
* list of followed sites.
* Design doc here: https://docs.google.com/document/d/1D-ZfhGv9GFLXHYKzAqsaw-LiVhsENRTJC5ZMaZ9z0sQ/
* edit#heading=h.p79wagdgjgx6
*/
public class FeedManagementMediator {
private static final String TAG = "FeedManagementMdtr";
private ModelList mModelList;
private final Activity mActivity;
private final FollowManagementLauncher mFollowManagementLauncher;
private final AutoplayManagementLauncher mAutoplayManagementLauncher;
private final @StreamKind int mInitiatingStreamKind;
private CustomTabsClient mClient;
private CustomTabsSession mCustomTabsSession;
/**
* Interface to supply a method which can launch the FollowManagementActivity.
*/
public interface FollowManagementLauncher {
void launchFollowManagement(Context mContext);
}
/**
* Interface to supply a method which can launch the AutoplayManagementActivity.
*/
public interface AutoplayManagementLauncher {
void launchAutoplayManagement(Context mContext);
}
FeedManagementMediator(Activity activity, ModelList modelList,
FollowManagementLauncher followLauncher, AutoplayManagementLauncher autoplayLauncher,
@StreamKind int initiatingStreamKind) {
mModelList = modelList;
mActivity = activity;
mFollowManagementLauncher = followLauncher;
mAutoplayManagementLauncher = autoplayLauncher;
mInitiatingStreamKind = initiatingStreamKind;
// Add the menu items into the menu.
PropertyModel activityModel = generateListItem(R.string.feed_manage_activity,
R.string.feed_manage_activity_description, this::handleActivityClick);
mModelList.add(new ModelListAdapter.ListItem(
FeedManagementItemProperties.DEFAULT_ITEM_TYPE, activityModel));
int descResource = ChromeFeatureList.isEnabled(ChromeFeatureList.FEED_FOLLOW_UI_UPDATE)
? R.string.feed_manage_interests_description_ui_update
: R.string.feed_manage_interests_description;
PropertyModel interestsModel = generateListItem(
R.string.feed_manage_interests, descResource, this::handleInterestsClick);
mModelList.add(new ModelListAdapter.ListItem(
FeedManagementItemProperties.DEFAULT_ITEM_TYPE, interestsModel));
PropertyModel hiddenModel = generateListItem(R.string.feed_manage_hidden,
R.string.feed_manage_hidden_description, this::handleHiddenClick);
mModelList.add(new ModelListAdapter.ListItem(
FeedManagementItemProperties.DEFAULT_ITEM_TYPE, hiddenModel));
if (FeedServiceBridge.isAutoplayEnabled()) {
PropertyModel autoplayModel = generateListItem(R.string.feed_manage_autoplay,
R.string.feed_manage_autoplay_description, this::handleAutoplayClick);
mModelList.add(new ModelListAdapter.ListItem(
FeedManagementItemProperties.DEFAULT_ITEM_TYPE, autoplayModel));
}
PropertyModel followingModel = generateListItem(R.string.feed_manage_following,
R.string.feed_manage_following_description, this::handleFollowingClick);
mModelList.add(new ModelListAdapter.ListItem(
FeedManagementItemProperties.DEFAULT_ITEM_TYPE, followingModel));
}
private PropertyModel generateListItem(
int titleResource, int descriptionResource, OnClickListener listener) {
String title = mActivity.getResources().getString(titleResource);
String description = mActivity.getResources().getString(descriptionResource);
return new PropertyModel.Builder(FeedManagementItemProperties.ALL_KEYS)
.with(FeedManagementItemProperties.TITLE_KEY, title)
.with(FeedManagementItemProperties.DESCRIPTION_KEY, description)
.with(FeedManagementItemProperties.ON_CLICK_KEY, listener)
.build();
}
// TODO(petewil): Borrowed these from code we can't link to. How do I keep them in sync?
static final String TRUSTED_APPLICATION_CODE_EXTRA = "trusted_application_code_extra";
// TODO(katzz): Replace with intent extras to be defined in AndroidX;
static final String EXTRA_ACTIVITY_INITIAL_WIDTH_PX =
"androidx.browser.customtabs.extra.INITIAL_ACTIVITY_WIDTH_PX";
static final String EXTRA_ACTIVITY_INITIAL_HEIGHT_PX =
"androidx.browser.customtabs.extra.INITIAL_ACTIVITY_HEIGHT_PX";
static final String EXTRA_ACTIVITY_SIDE_SHEET_BREAKPOINT_DP =
"androidx.browser.customtabs.extra.ACTIVITY_SIDE_SHEET_BREAKPOINT_DP";
static final String EXTRA_ACTIVITY_SIDE_SHEET_ENABLE_MAXIMIZATION =
"androidx.browser.customtabs.extra.ACTIVITY_SIDE_SHEET_ENABLE_MAXIMIZATION";
// Launch a new activity in the same task with the given uri as a CCT.
private void launchUriActivity(String uri) {
CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder();
builder.setShowTitle(true);
builder.setShareState(CustomTabsIntent.SHARE_STATE_ON);
if (ChromeFeatureList.sCctResizableSideSheetDiscoverFeedSettings.isEnabled()) {
int displayHeight = getDisplayMetrics().heightPixels;
int displayWidth = getDisplayMetrics().widthPixels;
int initialHeight = Math.max(displayHeight, displayWidth);
int initialWidth = Math.max(displayHeight, displayWidth) / 2;
int breakPoint =
(int) (Math.min(displayHeight, displayWidth) / getDisplayMetrics().density + 1);
CustomTabsIntent customTabsIntent = builder.build();
customTabsIntent.intent.setPackage(mActivity.getPackageName());
// Adding trusted extras lets us know that the intent came from Chrome.
customTabsIntent.intent.putExtra(
TRUSTED_APPLICATION_CODE_EXTRA, getAuthenticationToken());
customTabsIntent.intent.setData(Uri.parse(uri));
customTabsIntent.intent.putExtra(EXTRA_ACTIVITY_INITIAL_HEIGHT_PX, initialHeight);
customTabsIntent.intent.putExtra(EXTRA_ACTIVITY_INITIAL_WIDTH_PX, initialWidth);
customTabsIntent.intent.putExtra(EXTRA_ACTIVITY_SIDE_SHEET_BREAKPOINT_DP, breakPoint);
customTabsIntent.intent.putExtra(EXTRA_ACTIVITY_SIDE_SHEET_ENABLE_MAXIMIZATION, true);
customTabsIntent.launchUrl(mActivity, Uri.parse(uri));
} else {
Intent intent = builder.build().intent;
intent.setPackage(mActivity.getPackageName());
// Adding trusted extras lets us know that the intent came from Chrome.
intent.putExtra(TRUSTED_APPLICATION_CODE_EXTRA, getAuthenticationToken());
intent.setData(Uri.parse(uri));
intent.setAction(Intent.ACTION_VIEW);
intent.setClassName(
mActivity, "org.chromium.chrome.browser.customtabs.CustomTabActivity");
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // Needed for pre-N versions of android.
intent.putExtra(Browser.EXTRA_APPLICATION_ID, mActivity.getPackageName());
mActivity.startActivity(intent);
}
// TODO(https://crbug.com/1195209): Record uma by calling ReportOtherUserAction
// on the stream.
}
// The #getRealMetrics() method will give the physical size of the screen, which is
// generally fine when the app is not in multi-window mode and #getMetrics() will give the
// height excludes the decor views, so not suitable for our case. But in multi-window mode,
// we have no much choice, the closest way is to use #getMetrics() method.
private DisplayMetrics getDisplayMetrics() {
DisplayMetrics displayMetrics = new DisplayMetrics();
if (ApiCompatibilityUtils.isInMultiWindowMode(mActivity)) {
mActivity.getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
} else {
mActivity.getWindowManager().getDefaultDisplay().getRealMetrics(displayMetrics);
}
return displayMetrics;
}
// Copied from IntentHandler, which is in chrome_java, so we can't call it directly.
private PendingIntent getAuthenticationToken() {
Intent fakeIntent = new Intent();
ComponentName fakeComponentName =
new ComponentName(mActivity.getPackageName(), "FakeClass");
fakeIntent.setComponent(fakeComponentName);
int mutabililtyFlag = 0;
mutabililtyFlag = ApiHelperForM.getPendingIntentImmutableFlag();
return PendingIntent.getActivity(mActivity, 0, fakeIntent, mutabililtyFlag);
}
@VisibleForTesting
void handleActivityClick(View view) {
Log.d(TAG, "Activity click caught." + mInitiatingStreamKind);
FeedServiceBridge.reportOtherUserAction(
mInitiatingStreamKind, FeedUserActionType.TAPPED_MANAGE_ACTIVITY);
launchUriActivity("https://myactivity.google.com/myactivity?product=50");
}
@VisibleForTesting
void handleInterestsClick(View view) {
Log.d(TAG, "Interests click caught.");
FeedServiceBridge.reportOtherUserAction(
mInitiatingStreamKind, FeedUserActionType.TAPPED_MANAGE_INTERESTS);
launchUriActivity("https://www.google.com/preferences/interests/yourinterests?sh=n");
}
@VisibleForTesting
void handleHiddenClick(View view) {
Log.d(TAG, "Hidden click caught.");
FeedServiceBridge.reportOtherUserAction(
mInitiatingStreamKind, FeedUserActionType.TAPPED_MANAGE_INTERESTS);
launchUriActivity("https://www.google.com/preferences/interests/hidden?sh=n");
}
@VisibleForTesting
void handleAutoplayClick(View view) {
Log.d(TAG, "Autoplay click caught.");
FeedServiceBridge.reportOtherUserAction(
mInitiatingStreamKind, FeedUserActionType.OPENED_AUTOPLAY_SETTINGS);
mAutoplayManagementLauncher.launchAutoplayManagement(mActivity);
}
@VisibleForTesting
void handleFollowingClick(View view) {
Log.d(TAG, "Following click caught.");
FeedServiceBridge.reportOtherUserAction(
mInitiatingStreamKind, FeedUserActionType.TAPPED_MANAGE_FOLLOWING);
mFollowManagementLauncher.launchFollowManagement(mActivity);
}
}