1 // Copyright 2020 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 package org.chromium.chrome.browser.app.video_tutorials;
6 
7 import android.content.Context;
8 import android.content.Intent;
9 import android.view.ViewStub;
10 
11 import androidx.annotation.VisibleForTesting;
12 
13 import org.chromium.base.Callback;
14 import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
15 import org.chromium.chrome.browser.flags.ChromeFeatureList;
16 import org.chromium.chrome.browser.image_fetcher.ImageFetcher;
17 import org.chromium.chrome.browser.image_fetcher.ImageFetcherConfig;
18 import org.chromium.chrome.browser.image_fetcher.ImageFetcherFactory;
19 import org.chromium.chrome.browser.profiles.Profile;
20 import org.chromium.chrome.browser.video_tutorials.FeatureType;
21 import org.chromium.chrome.browser.video_tutorials.Tutorial;
22 import org.chromium.chrome.browser.video_tutorials.VideoTutorialService;
23 import org.chromium.chrome.browser.video_tutorials.VideoTutorialServiceFactory;
24 import org.chromium.chrome.browser.video_tutorials.iph.VideoIPHCoordinator;
25 import org.chromium.chrome.browser.video_tutorials.iph.VideoTutorialIPHUtils;
26 import org.chromium.chrome.browser.video_tutorials.metrics.VideoTutorialMetrics;
27 import org.chromium.chrome.browser.video_tutorials.metrics.VideoTutorialMetrics.UserAction;
28 import org.chromium.components.browser_ui.util.GlobalDiscardableReferencePool;
29 import org.chromium.components.feature_engagement.Tracker;
30 
31 import java.util.ArrayList;
32 import java.util.List;
33 
34 /**
35  * Handles all the logic required for showing the appropriate video tutorial IPH on new tab page.
36  * Queries the backend for the sorted list of available video tutorials, and shows the one not seen
37  * by user yet. Also responsible for showing the next tutorial IPH when one is consumed or
38  * dismissed.
39  */
40 public class NewTabPageVideoIPHManager {
41     private Context mContext;
42     private Tracker mTracker;
43     private VideoIPHCoordinator mVideoIPHCoordinator;
44     private VideoTutorialService mVideoTutorialService;
45 
46     /**
47      * Constructor.
48      * @param viewStub The {@link ViewStub} to be inflated to show the IPH.
49      * @param profile The associated profile.
50      */
NewTabPageVideoIPHManager(ViewStub viewStub, Profile profile)51     public NewTabPageVideoIPHManager(ViewStub viewStub, Profile profile) {
52         if (!ChromeFeatureList.isEnabled(ChromeFeatureList.VIDEO_TUTORIALS)) return;
53 
54         mContext = viewStub.getContext();
55         mTracker = TrackerFactory.getTrackerForProfile(profile);
56         mVideoIPHCoordinator = createVideoIPHCoordinator(
57                 viewStub, createImageFetcher(profile), this::onClickIPH, this::onDismissIPH);
58         mVideoTutorialService = VideoTutorialServiceFactory.getForProfile(profile);
59         mVideoTutorialService.getTutorials(this::onFetchTutorials);
60     }
61 
onFetchTutorials(List<Tutorial> tutorials)62     private void onFetchTutorials(List<Tutorial> tutorials) {
63         if (tutorials.isEmpty()) return;
64 
65         // Add the summary tutorial to the list.
66         List<Tutorial> tutorialsCopy = new ArrayList<>(tutorials);
67         mVideoTutorialService.getTutorial(FeatureType.SUMMARY, tutorial -> {
68             if (tutorial != null) tutorialsCopy.add(tutorial);
69 
70             mTracker.addOnInitializedCallback(success -> {
71                 if (!success) return;
72                 showFirstEligibleIPH(tutorialsCopy);
73             });
74         });
75     }
76 
showFirstEligibleIPH(List<Tutorial> tutorials)77     private void showFirstEligibleIPH(List<Tutorial> tutorials) {
78         for (Tutorial tutorial : tutorials) {
79             String featureName = VideoTutorialIPHUtils.getFeatureNameForNTP(tutorial.featureType);
80             if (featureName == null) continue;
81             if (mTracker.shouldTriggerHelpUI(featureName)) {
82                 VideoTutorialMetrics.recordUserAction(
83                         tutorial.featureType, UserAction.IPH_NTP_SHOWN);
84                 mVideoIPHCoordinator.showVideoIPH(tutorial);
85                 mTracker.dismissed(featureName);
86                 break;
87             }
88         }
89     }
90 
onClickIPH(Tutorial tutorial)91     private void onClickIPH(Tutorial tutorial) {
92         // TODO(shaktisahu): Maybe collect this event when video has been halfway watched.
93         mTracker.notifyEvent(VideoTutorialIPHUtils.getClickEvent(tutorial.featureType));
94         VideoTutorialMetrics.recordUserAction(tutorial.featureType, UserAction.IPH_NTP_CLICKED);
95 
96         // Bring up the player and start playing the video.
97         if (tutorial.featureType == FeatureType.SUMMARY) {
98             launchTutorialListActivity();
99         } else {
100             launchVideoPlayer(tutorial);
101         }
102     }
103 
onDismissIPH(Tutorial tutorial)104     private void onDismissIPH(Tutorial tutorial) {
105         mTracker.notifyEvent(VideoTutorialIPHUtils.getDismissEvent(tutorial.featureType));
106         VideoTutorialMetrics.recordUserAction(tutorial.featureType, UserAction.IPH_NTP_DISMISSED);
107 
108         // TODO(shaktisahu): Animate this. Maybe add a delay.
109         mVideoTutorialService.getTutorials(this::onFetchTutorials);
110     }
111 
112     @VisibleForTesting
launchVideoPlayer(Tutorial tutorial)113     protected void launchVideoPlayer(Tutorial tutorial) {
114         VideoPlayerActivity.playVideoTutorial(mContext, tutorial.featureType);
115     }
116 
117     @VisibleForTesting
launchTutorialListActivity()118     protected void launchTutorialListActivity() {
119         Intent intent = new Intent();
120         intent.setClass(mContext, VideoTutorialListActivity.class);
121         mContext.startActivity(intent);
122     }
123 
124     @VisibleForTesting
createImageFetcher(Profile profile)125     protected ImageFetcher createImageFetcher(Profile profile) {
126         return ImageFetcherFactory.createImageFetcher(ImageFetcherConfig.IN_MEMORY_WITH_DISK_CACHE,
127                 profile, GlobalDiscardableReferencePool.getReferencePool());
128     }
129 
130     @VisibleForTesting
createVideoIPHCoordinator(ViewStub viewStub, ImageFetcher imageFetcher, Callback<Tutorial> clickListener, Callback<Tutorial> dismissListener)131     protected VideoIPHCoordinator createVideoIPHCoordinator(ViewStub viewStub,
132             ImageFetcher imageFetcher, Callback<Tutorial> clickListener,
133             Callback<Tutorial> dismissListener) {
134         return VideoTutorialServiceFactory.createVideoIPHCoordinator(
135                 viewStub, imageFetcher, clickListener, dismissListener);
136     }
137 }
138