Merge "Add event tracking to the gesture navigation tutorial." into tm-dev am: e0380381b7

Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Launcher3/+/17127449

Change-Id: Ia417f3d407c7ef254e7bf31f05782f13294a7345
This commit is contained in:
TreeHugger Robot
2022-03-15 22:37:51 +00:00
committed by Automerger Merge Worker
9 changed files with 198 additions and 32 deletions
@@ -15,13 +15,21 @@
*/
package com.android.quickstep.interaction;
import android.content.SharedPreferences;
import android.view.MotionEvent;
import android.view.View;
import com.android.launcher3.logging.StatsLogManager;
import com.android.quickstep.interaction.TutorialController.TutorialType;
/** Shows the Home gesture interactive tutorial. */
public class AssistantGestureTutorialFragment extends TutorialFragment {
protected AssistantGestureTutorialFragment(
SharedPreferences sharedPrefs, StatsLogManager statsLogManager) {
super(sharedPrefs, statsLogManager);
}
@Override
TutorialController createController(TutorialType type) {
return new AssistantGestureTutorialController(this, type);
@@ -39,4 +47,14 @@ public class AssistantGestureTutorialFragment extends TutorialFragment {
}
return super.onTouch(view, motionEvent);
}
@Override
void logTutorialStepShown() {
// No-Op: tutorial step not currently shown to users
}
@Override
void logTutorialStepCompleted() {
// No-Op: tutorial step not currently shown to users
}
}
@@ -19,12 +19,14 @@ import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.SharedPreferences;
import android.view.MotionEvent;
import android.view.View;
import androidx.annotation.Nullable;
import com.android.launcher3.R;
import com.android.launcher3.logging.StatsLogManager;
import com.android.quickstep.interaction.TutorialController.TutorialType;
import java.util.ArrayList;
@@ -32,6 +34,11 @@ import java.util.ArrayList;
/** Shows the Back gesture interactive tutorial. */
public class BackGestureTutorialFragment extends TutorialFragment {
protected BackGestureTutorialFragment(
SharedPreferences sharedPrefs, StatsLogManager statsLogManager) {
super(sharedPrefs, statsLogManager);
}
@Nullable
@Override
Integer getEdgeAnimationResId() {
@@ -117,4 +124,16 @@ public class BackGestureTutorialFragment extends TutorialFragment {
}
return super.onTouch(view, motionEvent);
}
@Override
void logTutorialStepShown() {
mStatsLogManager.logger().log(
StatsLogManager.LauncherEvent.LAUNCHER_GESTURE_TUTORIAL_BACK_STEP_SHOWN);
}
@Override
void logTutorialStepCompleted() {
mStatsLogManager.logger().log(
StatsLogManager.LauncherEvent.LAUNCHER_GESTURE_TUTORIAL_BACK_STEP_COMPLETED);
}
}
@@ -15,6 +15,7 @@
*/
package com.android.quickstep.interaction;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.graphics.Rect;
import android.os.Bundle;
@@ -28,6 +29,8 @@ import androidx.annotation.NonNull;
import androidx.fragment.app.FragmentActivity;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.logging.StatsLogManager;
import com.android.quickstep.interaction.TutorialController.TutorialType;
import java.util.List;
@@ -46,17 +49,26 @@ public class GestureSandboxActivity extends FragmentActivity {
private int mCurrentStep;
private int mNumSteps;
private SharedPreferences mSharedPrefs;
private StatsLogManager mStatsLogManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.gesture_tutorial_activity);
mSharedPrefs = Utilities.getPrefs(this);
mStatsLogManager = StatsLogManager.newInstance(getApplicationContext());
Bundle args = savedInstanceState == null ? getIntent().getExtras() : savedInstanceState;
mTutorialSteps = getTutorialSteps(args);
mCurrentTutorialStep = mTutorialSteps[mCurrentStep - 1];
mFragment = TutorialFragment.newInstance(
mCurrentTutorialStep, args.getBoolean(KEY_GESTURE_COMPLETE, false));
mCurrentTutorialStep,
args.getBoolean(KEY_GESTURE_COMPLETE, false),
mSharedPrefs,
mStatsLogManager);
getSupportFragmentManager().beginTransaction()
.add(R.id.gesture_tutorial_fragment_container, mFragment)
.commit();
@@ -104,13 +116,6 @@ public class GestureSandboxActivity extends FragmentActivity {
return mNumSteps;
}
/**
* Closes the tutorial and this activity.
*/
public void closeTutorial() {
mFragment.closeTutorial();
}
/**
* Replaces the current TutorialFragment, continuing to the next tutorial step if there is one.
*
@@ -122,7 +127,8 @@ public class GestureSandboxActivity extends FragmentActivity {
return;
}
mCurrentTutorialStep = mTutorialSteps[mCurrentStep];
mFragment = TutorialFragment.newInstance(mCurrentTutorialStep, false);
mFragment = TutorialFragment.newInstance(
mCurrentTutorialStep, /* gestureComplete= */ false, mSharedPrefs, mStatsLogManager);
getSupportFragmentManager().beginTransaction()
.replace(R.id.gesture_tutorial_fragment_container, mFragment)
.runOnCommit(() -> mFragment.onAttachedToWindow())
@@ -18,12 +18,14 @@ package com.android.quickstep.interaction;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.content.SharedPreferences;
import android.view.MotionEvent;
import android.view.View;
import androidx.annotation.Nullable;
import com.android.launcher3.R;
import com.android.launcher3.logging.StatsLogManager;
import com.android.quickstep.interaction.TutorialController.TutorialType;
import java.util.ArrayList;
@@ -31,6 +33,11 @@ import java.util.ArrayList;
/** Shows the Home gesture interactive tutorial. */
public class HomeGestureTutorialFragment extends TutorialFragment {
protected HomeGestureTutorialFragment(
SharedPreferences sharedPrefs, StatsLogManager statsLogManager) {
super(sharedPrefs, statsLogManager);
}
@Nullable
@Override
Integer getEdgeAnimationResId() {
@@ -99,4 +106,16 @@ public class HomeGestureTutorialFragment extends TutorialFragment {
releaseFeedbackAnimation();
return super.onTouch(view, motionEvent);
}
@Override
void logTutorialStepShown() {
mStatsLogManager.logger().log(
StatsLogManager.LauncherEvent.LAUNCHER_GESTURE_TUTORIAL_HOME_STEP_SHOWN);
}
@Override
void logTutorialStepCompleted() {
mStatsLogManager.logger().log(
StatsLogManager.LauncherEvent.LAUNCHER_GESTURE_TUTORIAL_HOME_STEP_COMPLETED);
}
}
@@ -18,12 +18,14 @@ package com.android.quickstep.interaction;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.content.SharedPreferences;
import android.view.MotionEvent;
import android.view.View;
import androidx.annotation.Nullable;
import com.android.launcher3.R;
import com.android.launcher3.logging.StatsLogManager;
import com.android.quickstep.interaction.TutorialController.TutorialType;
import java.util.ArrayList;
@@ -31,6 +33,11 @@ import java.util.ArrayList;
/** Shows the Overview gesture interactive tutorial. */
public class OverviewGestureTutorialFragment extends TutorialFragment {
protected OverviewGestureTutorialFragment(
SharedPreferences sharedPrefs, StatsLogManager statsLogManager) {
super(sharedPrefs, statsLogManager);
}
@Nullable
@Override
Integer getEdgeAnimationResId() {
@@ -111,4 +118,16 @@ public class OverviewGestureTutorialFragment extends TutorialFragment {
releaseFeedbackAnimation();
return super.onTouch(view, motionEvent);
}
@Override
void logTutorialStepShown() {
mStatsLogManager.logger().log(
StatsLogManager.LauncherEvent.LAUNCHER_GESTURE_TUTORIAL_OVERVIEW_STEP_SHOWN);
}
@Override
void logTutorialStepCompleted() {
mStatsLogManager.logger().log(
StatsLogManager.LauncherEvent.LAUNCHER_GESTURE_TUTORIAL_OVERVIEW_STEP_COMPLETED);
}
}
@@ -15,14 +15,21 @@
*/
package com.android.quickstep.interaction;
import android.content.SharedPreferences;
import android.view.MotionEvent;
import android.view.View;
import com.android.launcher3.logging.StatsLogManager;
import com.android.quickstep.interaction.TutorialController.TutorialType;
/** Shows the general navigation gesture sandbox environment. */
public class SandboxModeTutorialFragment extends TutorialFragment {
protected SandboxModeTutorialFragment(
SharedPreferences sharedPrefs, StatsLogManager statsLogManager) {
super(sharedPrefs, statsLogManager);
}
@Override
TutorialController createController(TutorialType type) {
return new SandboxModeTutorialController(this, type);
@@ -40,4 +47,14 @@ public class SandboxModeTutorialFragment extends TutorialFragment {
}
return super.onTouch(view, motionEvent);
}
@Override
void logTutorialStepShown() {
// No-Op: tutorial step not currently shown to users
}
@Override
void logTutorialStepCompleted() {
// No-Op: tutorial step not currently shown to users
}
}
@@ -81,7 +81,8 @@ abstract class TutorialController implements BackGestureAttemptCallback,
TutorialType mTutorialType;
final Context mContext;
final TextView mCloseButton;
final TextView mSkipButton;
final Button mDoneButton;
final ViewGroup mFeedbackView;
final TextView mFeedbackTitleView;
final ImageView mEdgeGestureVideoView;
@@ -94,7 +95,6 @@ abstract class TutorialController implements BackGestureAttemptCallback,
final AnimatedTaskView mFakePreviousTaskView;
final View mRippleView;
final RippleDrawable mRippleDrawable;
final Button mActionButton;
final TutorialStepIndicator mTutorialStepView;
final ImageView mFingerDotView;
private final AlertDialog mSkipTutorialDialog;
@@ -115,8 +115,8 @@ abstract class TutorialController implements BackGestureAttemptCallback,
mContext = mTutorialFragment.getContext();
RootSandboxLayout rootView = tutorialFragment.getRootView();
mCloseButton = rootView.findViewById(R.id.gesture_tutorial_fragment_close_button);
mCloseButton.setOnClickListener(button -> showSkipTutorialDialog());
mSkipButton = rootView.findViewById(R.id.gesture_tutorial_fragment_close_button);
mSkipButton.setOnClickListener(button -> showSkipTutorialDialog());
mFeedbackView = rootView.findViewById(R.id.gesture_tutorial_fragment_feedback_view);
mFeedbackTitleView = mFeedbackView.findViewById(
R.id.gesture_tutorial_fragment_feedback_title);
@@ -130,7 +130,7 @@ abstract class TutorialController implements BackGestureAttemptCallback,
rootView.findViewById(R.id.gesture_tutorial_fake_previous_task_view);
mRippleView = rootView.findViewById(R.id.gesture_tutorial_ripple_view);
mRippleDrawable = (RippleDrawable) mRippleView.getBackground();
mActionButton = rootView.findViewById(R.id.gesture_tutorial_fragment_action_button);
mDoneButton = rootView.findViewById(R.id.gesture_tutorial_fragment_action_button);
mTutorialStepView =
rootView.findViewById(R.id.gesture_tutorial_fragment_feedback_tutorial_step);
mFingerDotView = rootView.findViewById(R.id.gesture_tutorial_finger_dot);
@@ -431,22 +431,22 @@ abstract class TutorialController implements BackGestureAttemptCallback,
}
void updateCloseButton() {
mCloseButton.setTextAppearance(Utilities.isDarkTheme(mContext)
mSkipButton.setTextAppearance(Utilities.isDarkTheme(mContext)
? R.style.TextAppearance_GestureTutorial_Feedback_Subtext
: R.style.TextAppearance_GestureTutorial_Feedback_Subtext_Dark);
}
void hideActionButton() {
mCloseButton.setVisibility(View.VISIBLE);
mSkipButton.setVisibility(View.VISIBLE);
// Invisible to maintain the layout.
mActionButton.setVisibility(View.INVISIBLE);
mActionButton.setOnClickListener(null);
mDoneButton.setVisibility(View.INVISIBLE);
mDoneButton.setOnClickListener(null);
}
void showActionButton() {
mCloseButton.setVisibility(GONE);
mActionButton.setVisibility(View.VISIBLE);
mActionButton.setOnClickListener(this::onActionButtonClicked);
mSkipButton.setVisibility(GONE);
mDoneButton.setVisibility(View.VISIBLE);
mDoneButton.setOnClickListener(this::onActionButtonClicked);
}
void hideFakeTaskbar(boolean animateToHotseat) {
@@ -609,7 +609,7 @@ abstract class TutorialController implements BackGestureAttemptCallback,
R.id.gesture_tutorial_dialog_confirm_button);
if (confirmButton != null) {
confirmButton.setOnClickListener(v -> {
sandboxActivity.closeTutorial();
mTutorialFragment.closeTutorial(true);
tutorialDialog.dismiss();
});
} else {
@@ -20,11 +20,13 @@ import android.animation.AnimatorListenerAdapter;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Insets;
import android.graphics.drawable.Animatable2;
import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.util.ArraySet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
@@ -43,14 +45,24 @@ import androidx.fragment.app.FragmentActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.logging.StatsLogManager;
import com.android.quickstep.interaction.TutorialController.TutorialType;
import java.util.Set;
abstract class TutorialFragment extends Fragment implements OnTouchListener {
private static final String LOG_TAG = "TutorialFragment";
static final String KEY_TUTORIAL_TYPE = "tutorial_type";
static final String KEY_GESTURE_COMPLETE = "gesture_complete";
private static final String TUTORIAL_SKIPPED_PREFERENCE_KEY = "pref_gestureTutorialSkipped";
private static final String COMPLETED_TUTORIAL_STEPS_PREFERENCE_KEY =
"pref_completedTutorialSteps";
private final SharedPreferences mSharedPrefs;
protected final StatsLogManager mStatsLogManager;
TutorialType mTutorialType;
boolean mGestureComplete = false;
@Nullable TutorialController mTutorialController = null;
@@ -71,10 +83,15 @@ abstract class TutorialFragment extends Fragment implements OnTouchListener {
private boolean mIsLargeScreen;
private boolean mIsFoldable;
public static TutorialFragment newInstance(TutorialType tutorialType, boolean gestureComplete) {
TutorialFragment fragment = getFragmentForTutorialType(tutorialType);
public static TutorialFragment newInstance(
TutorialType tutorialType,
boolean gestureComplete,
SharedPreferences sharedPrefs,
StatsLogManager statsLogManager) {
TutorialFragment fragment =
getFragmentForTutorialType(tutorialType, sharedPrefs, statsLogManager);
if (fragment == null) {
fragment = new BackGestureTutorialFragment();
fragment = new BackGestureTutorialFragment(sharedPrefs, statsLogManager);
tutorialType = TutorialType.BACK_NAVIGATION;
}
@@ -86,28 +103,36 @@ abstract class TutorialFragment extends Fragment implements OnTouchListener {
}
@Nullable
private static TutorialFragment getFragmentForTutorialType(TutorialType tutorialType) {
private static TutorialFragment getFragmentForTutorialType(
TutorialType tutorialType,
SharedPreferences sharedPrefs,
StatsLogManager statsLogManager) {
switch (tutorialType) {
case BACK_NAVIGATION:
case BACK_NAVIGATION_COMPLETE:
return new BackGestureTutorialFragment();
return new BackGestureTutorialFragment(sharedPrefs, statsLogManager);
case HOME_NAVIGATION:
case HOME_NAVIGATION_COMPLETE:
return new HomeGestureTutorialFragment();
return new HomeGestureTutorialFragment(sharedPrefs, statsLogManager);
case OVERVIEW_NAVIGATION:
case OVERVIEW_NAVIGATION_COMPLETE:
return new OverviewGestureTutorialFragment();
return new OverviewGestureTutorialFragment(sharedPrefs, statsLogManager);
case ASSISTANT:
case ASSISTANT_COMPLETE:
return new AssistantGestureTutorialFragment();
return new AssistantGestureTutorialFragment(sharedPrefs, statsLogManager);
case SANDBOX_MODE:
return new SandboxModeTutorialFragment();
return new SandboxModeTutorialFragment(sharedPrefs, statsLogManager);
default:
Log.e(LOG_TAG, "Failed to find an appropriate fragment for " + tutorialType.name());
}
return null;
}
protected TutorialFragment(SharedPreferences sharedPrefs, StatsLogManager statsLogManager) {
mSharedPrefs = sharedPrefs;
mStatsLogManager = statsLogManager;
}
@Nullable Integer getEdgeAnimationResId() {
return null;
}
@@ -315,6 +340,7 @@ abstract class TutorialFragment extends Fragment implements OnTouchListener {
}
void onAttachedToWindow() {
logTutorialStepShown();
mEdgeBackGestureHandler.setViewGroupParent(getRootView());
}
@@ -348,8 +374,16 @@ abstract class TutorialFragment extends Fragment implements OnTouchListener {
}
void continueTutorial() {
GestureSandboxActivity gestureSandboxActivity = getGestureSandboxActivity();
Set<String> updatedCompletedSteps = new ArraySet<>(mSharedPrefs.getStringSet(
COMPLETED_TUTORIAL_STEPS_PREFERENCE_KEY, new ArraySet<>()));
updatedCompletedSteps.add(mTutorialType.toString());
mSharedPrefs.edit().putStringSet(
COMPLETED_TUTORIAL_STEPS_PREFERENCE_KEY, updatedCompletedSteps).apply();
logTutorialStepCompleted();
GestureSandboxActivity gestureSandboxActivity = getGestureSandboxActivity();
if (gestureSandboxActivity == null) {
closeTutorial();
return;
@@ -358,6 +392,15 @@ abstract class TutorialFragment extends Fragment implements OnTouchListener {
}
void closeTutorial() {
closeTutorial(false);
}
void closeTutorial(boolean tutorialSkipped) {
if (tutorialSkipped) {
mSharedPrefs.edit().putBoolean(TUTORIAL_SKIPPED_PREFERENCE_KEY, true).apply();
mStatsLogManager.logger().log(
StatsLogManager.LauncherEvent.LAUNCHER_GESTURE_TUTORIAL_SKIPPED);
}
FragmentActivity activity = getActivity();
if (activity != null) {
activity.setResult(Activity.RESULT_OK);
@@ -390,6 +433,10 @@ abstract class TutorialFragment extends Fragment implements OnTouchListener {
|| (mTutorialController != null && mTutorialController.isGestureCompleted());
}
abstract void logTutorialStepShown();
abstract void logTutorialStepCompleted();
@Nullable
private GestureSandboxActivity getGestureSandboxActivity() {
Context context = getContext();
@@ -529,6 +529,27 @@ public class StatsLogManager implements ResourceBasedOverride {
@UiEvent(doc = "User clicks on the search icon on header to launch search in app.")
LAUNCHER_ALLAPPS_SEARCHINAPP_LAUNCH(913),
@UiEvent(doc = "User is shown the back gesture navigation tutorial step.")
LAUNCHER_GESTURE_TUTORIAL_BACK_STEP_SHOWN(959),
@UiEvent(doc = "User is shown the home gesture navigation tutorial step.")
LAUNCHER_GESTURE_TUTORIAL_HOME_STEP_SHOWN(960),
@UiEvent(doc = "User is shown the overview gesture navigation tutorial step.")
LAUNCHER_GESTURE_TUTORIAL_OVERVIEW_STEP_SHOWN(961),
@UiEvent(doc = "User completed the back gesture navigation tutorial step.")
LAUNCHER_GESTURE_TUTORIAL_BACK_STEP_COMPLETED(962),
@UiEvent(doc = "User completed the home gesture navigation tutorial step.")
LAUNCHER_GESTURE_TUTORIAL_HOME_STEP_COMPLETED(963),
@UiEvent(doc = "User completed the overview gesture navigation tutorial step.")
LAUNCHER_GESTURE_TUTORIAL_OVERVIEW_STEP_COMPLETED(964),
@UiEvent(doc = "User skips the gesture navigation tutorial.")
LAUNCHER_GESTURE_TUTORIAL_SKIPPED(965),
@UiEvent(doc = "User scrolled on one of the all apps surfaces such as A-Z list, search "
+ "result page etc.")
LAUNCHER_ALLAPPS_SCROLLED(985);