| /* |
| * Copyright (C) 2018 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.example.android.intentplayground; |
| |
| import android.content.Context; |
| import android.content.res.Resources; |
| import android.graphics.drawable.Drawable; |
| import android.os.Bundle; |
| import android.util.DisplayMetrics; |
| import android.view.Gravity; |
| import android.view.LayoutInflater; |
| import android.view.View; |
| import android.view.ViewGroup; |
| import android.widget.FrameLayout; |
| import android.widget.ScrollView; |
| |
| import androidx.annotation.IdRes; |
| import androidx.annotation.Nullable; |
| import androidx.annotation.StringRes; |
| import androidx.fragment.app.Fragment; |
| import androidx.viewpager.widget.PagerTitleStrip; |
| import androidx.viewpager.widget.ViewPager; |
| import java.util.LinkedList; |
| import java.util.List; |
| |
| /** |
| * Displays a help overlay over the current activity. |
| */ |
| public class ShowcaseFragment extends Fragment { |
| private ViewGroup mRoot; |
| private List<Step> mSteps = new LinkedList<>(); |
| private ViewPager mPager; |
| private StepAdapter mAdapter; |
| private ScrollView mScrollView; |
| private View mOldTarget; |
| private Drawable mOldTargetBackground; |
| private DisplayMetrics mDisplayMetrics = new DisplayMetrics(); |
| private Runnable mUserOnFinish; |
| private int mIndex = 0; |
| private static final int SCROLL_OFFSET = 50; |
| private static final float HIGHLIGHT_ELEVATION = 4; |
| |
| @Override |
| public void onAttach(Context context) { |
| super.onAttach(context); |
| mAdapter = new StepAdapter(context, mSteps); |
| } |
| |
| @Nullable |
| @Override |
| public View onCreateView(LayoutInflater inflater, |
| @Nullable ViewGroup container, |
| @Nullable Bundle savedInstanceState) { |
| Context context = getContext(); |
| mRoot = container; |
| FrameLayout backgroundLayout = new FrameLayout(context); |
| mPager = new ViewPager(context); |
| PagerTitleStrip pagerTitleView = new PagerTitleStrip(context); |
| pagerTitleView.setGravity(Gravity.TOP); |
| ViewPager.LayoutParams params = new ViewPager.LayoutParams(); |
| params.width = ViewPager.LayoutParams.MATCH_PARENT; |
| params.height = ViewPager.LayoutParams.MATCH_PARENT; |
| mPager.setLayoutParams(params); |
| backgroundLayout.setLayoutParams(params); |
| params.height = ViewPager.LayoutParams.WRAP_CONTENT; |
| params.isDecor = true; |
| pagerTitleView.setLayoutParams(params); |
| mPager.addView(pagerTitleView); |
| backgroundLayout.addView(mPager); |
| mAdapter.setButtonCallbacks( |
| /* onFinish */ view -> { |
| cancel(); |
| mScrollView.scrollTo(0, 0); |
| }, |
| /* onCancel */ view -> cancel(), |
| /* onNext */ view -> next() |
| ); |
| mPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { |
| @Override |
| public void onPageScrolled(int i, float v, int i1) {} |
| @Override |
| public void onPageScrollStateChanged(int i) {} |
| @Override |
| public void onPageSelected(int i) { |
| executeStep(i); |
| } |
| }); |
| mPager.setAdapter(mAdapter); |
| return backgroundLayout; |
| } |
| |
| @Override |
| public void onStart() { |
| super.onStart(); |
| // Get display metrics for converting dp to px |
| getActivity().getWindowManager().getDefaultDisplay().getMetrics(mDisplayMetrics); |
| // Scroll to and highlight first step |
| executeStep(mIndex); |
| } |
| |
| @Override |
| public void onStop() { |
| super.onStop(); |
| clearHighlight(); |
| if (mUserOnFinish != null) mUserOnFinish.run(); |
| } |
| |
| public void setScroller(ScrollView scroller) { |
| mScrollView = scroller; |
| } |
| |
| public void addStep(Step step) { |
| mSteps.add(step); |
| if (mAdapter != null) mAdapter.notifyDataSetChanged(); |
| } |
| |
| public void addStep(@StringRes int tutorialText, @IdRes int targetView) { |
| addStep(new Step(tutorialText, targetView)); |
| } |
| |
| public void addStep(@StringRes int tutorialText, @IdRes int targetView, |
| @IdRes int highlightTargetView) { |
| addStep(new Step(tutorialText, targetView, highlightTargetView)); |
| } |
| |
| public void addStep(@StringRes int tutorialText, |
| @IdRes int targetView, |
| Runnable callback) { |
| addStep(new Step(tutorialText, targetView, callback)); |
| } |
| |
| /** |
| * Advances the pager to the next step. |
| */ |
| public void next() { |
| mPager.setCurrentItem(++mIndex); |
| } |
| |
| /** |
| * Shows the indicated page. |
| * @param i The index of the page to show. |
| */ |
| private void executeStep(int i) { |
| Step current = mAdapter.getStep(i); |
| View target = mRoot.findViewById(current.targetViewRes); |
| View highlightTarget = current.highlightTargetViewRes != 0 ? |
| mRoot.findViewById(current.highlightTargetViewRes) : target; |
| target.getParent().requestChildFocus(target, target); |
| mScrollView.smoothScrollTo(0, Float.valueOf(target.getTop()).intValue() |
| - SCROLL_OFFSET); |
| highlightView(highlightTarget); |
| if (current.callback != null) current.callback.run(); |
| } |
| |
| /** |
| * Destroys this fragment. |
| */ |
| public void cancel() { |
| getActivity().getSupportFragmentManager().beginTransaction().remove(this).commit(); |
| } |
| |
| private void clearHighlight() { |
| if (mOldTarget != null) { |
| mOldTarget.setBackground(mOldTargetBackground); |
| mOldTarget = null; |
| } |
| mRoot.setBackground(null); // Clear root background |
| } |
| |
| private void highlightView(View target) { |
| Resources res = getContext().getResources(); |
| clearHighlight(); |
| mOldTarget = target; |
| mOldTargetBackground = target.getBackground(); |
| target.setBackground(res.getDrawable(R.drawable.showcase_background, null /* theme*/)); |
| target.setElevation(HIGHLIGHT_ELEVATION * mDisplayMetrics.density); |
| // Dull parent background |
| mRoot.setBackground(res.getDrawable(R.drawable.shade, null /* theme */)); |
| } |
| |
| /** |
| * Set a callback to be run in the onStop() method |
| * @param onFinish Callback to be run when the Showcase is finished |
| */ |
| public void setOnFinish(Runnable onFinish) { |
| this.mUserOnFinish = onFinish; |
| } |
| |
| /** |
| * Represents a page in {@link ViewPager}, with associated text to show and a target element |
| * to scroll to. |
| */ |
| public class Step { |
| @StringRes public int tutorialText; |
| @IdRes public int targetViewRes; |
| @IdRes public int highlightTargetViewRes; |
| |
| public Runnable callback; |
| public Step(@StringRes int tutorialSentence, @IdRes int targetView) { |
| tutorialText = tutorialSentence; |
| targetViewRes = targetView; |
| } |
| public Step(@StringRes int tutorialSentence, @IdRes int targetView, |
| @IdRes int highlightTargetView) { |
| tutorialText = tutorialSentence; |
| targetViewRes = targetView; |
| highlightTargetViewRes = highlightTargetView; |
| } |
| public Step(@StringRes int tutorialSentence, @IdRes int targetView, |
| Runnable onStepCallback) { |
| tutorialText = tutorialSentence; |
| targetViewRes = targetView; |
| callback = onStepCallback; |
| } |
| } |
| |
| } |