Last active
July 13, 2016 18:46
-
-
Save xtools-at/01759993ab84674769e5388f7b88e2e2 to your computer and use it in GitHub Desktop.
Android Introduction Overlay Class in CCL
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//copied from https://github.com/googlecast/CastCompanionLibrary-android | |
/* | |
* Copyright (C) 2015 Google Inc. All Rights Reserved. | |
* | |
* 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.google.android.libraries.cast.companionlibrary.widgets; | |
import com.google.android.libraries.cast.companionlibrary.R; | |
import com.google.android.libraries.cast.companionlibrary.utils.Utils; | |
import android.animation.Animator; | |
import android.animation.AnimatorListenerAdapter; | |
import android.animation.ObjectAnimator; | |
import android.annotation.TargetApi; | |
import android.app.Activity; | |
import android.content.Context; | |
import android.content.SharedPreferences; | |
import android.content.res.TypedArray; | |
import android.graphics.Bitmap; | |
import android.graphics.Canvas; | |
import android.graphics.Color; | |
import android.graphics.Paint; | |
import android.graphics.PorterDuff; | |
import android.graphics.PorterDuffXfermode; | |
import android.graphics.Rect; | |
import android.os.Build; | |
import android.preference.PreferenceManager; | |
import android.support.annotation.ColorRes; | |
import android.support.annotation.DimenRes; | |
import android.support.annotation.StringRes; | |
import android.support.v7.app.MediaRouteButton; | |
import android.text.TextUtils; | |
import android.util.AttributeSet; | |
import android.view.LayoutInflater; | |
import android.view.Menu; | |
import android.view.MenuItem; | |
import android.view.MotionEvent; | |
import android.view.View; | |
import android.view.ViewGroup; | |
import android.widget.Button; | |
import android.widget.RelativeLayout; | |
import android.widget.TextView; | |
/** | |
* A simple overlay view that can be used to bring user's attention to the cast button. To use | |
* this overlay, build an instance of this class and call {@link #show()}: | |
* <pre> | |
* IntroductoryOverlay overlay = new IntroductoryOverlay.Builder(activity) | |
* .setMenuItem(mMediaRouteMenuItem) | |
* .setTitleText(R.string.intro_overlay_text) | |
* .setOnDismissed(onOverlayDismissedListener) | |
* .setSingleSTime() | |
* .build(); | |
* overlay.show(); | |
* </pre> | |
* Here, {@code mMediaRouteMenuItem} is the {@link MenuItem} reference to the cast button (returned | |
* when you call | |
* {@link com.google.android.libraries.cast.companionlibrary.cast.VideoCastManager#addMediaRouterButton(Menu, int)}). | |
* You can also point to an instance of {@link MediaRouteButton} by calling | |
* {@link Builder#setMediaRouteButton(MediaRouteButton)}. | |
* <p>To customize the layout of this view, copy {@code res/layout/ccl_intro_overlay.xml} into your | |
* project and update that copy (keep the component ids the same). To customize the style, take a | |
* look at the {@code res/values/intro_overlay.xml}. | |
* <p>In order to show this overlay at the right time, clients can register to | |
* {@link com.google.android.libraries.cast.companionlibrary.cast.callbacks.VideoCastConsumer#onCastAvailabilityChanged(boolean)}. | |
* Management of how often this overlay should be shown is left to the client application. | |
*/ | |
public class IntroductoryOverlay extends RelativeLayout { | |
private static final long FADE_OUT_LENGTH_MS = 400; | |
public static final String FTU_SHOWN_KEY = "ccl_ftu_shown"; | |
private boolean mIsSingleTime; | |
private TextView mTitleText; | |
private TextView mSubtitleText; | |
private Button mButton; | |
private float mFocusRadius; | |
private int mOverlayColorId; | |
private int mCenterY; | |
private int mCenterX; | |
private Paint mHolePaint; | |
private Bitmap mBitmap; | |
private boolean mIsOverlayVisible; | |
private static final String ALPHA_PROPERTY = "alpha"; | |
private static final float INVISIBLE_VALUE = 0f; | |
private OnOverlayDismissedListener mListener; | |
private IntroductoryOverlay(Builder builder) { | |
this(builder, null, R.styleable.CustomTheme_CCLIntroOverlayStyle); | |
} | |
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) | |
public IntroductoryOverlay(Builder builder, AttributeSet attrs, int defStyleAttr) { | |
super(builder.mContext, attrs, defStyleAttr); | |
mIsSingleTime = builder.mSingleTime; | |
LayoutInflater inflater = LayoutInflater.from(getContext()); | |
inflater.inflate(R.layout.ccl_intro_overlay, this); | |
mButton = (Button) findViewById(R.id.button); | |
mTitleText = (TextView) findViewById(R.id.textTitle); | |
mSubtitleText = (TextView) findViewById(R.id.textSubtitle); | |
TypedArray typedArray = getContext().getTheme() | |
.obtainStyledAttributes(attrs, R.styleable.CCLIntroOverlay, | |
R.attr.CCLIntroOverlayStyle, R.style.CCLIntroOverlay); | |
if (builder.mOverlayColor != 0) { | |
mOverlayColorId = builder.mOverlayColor; | |
} else { | |
mOverlayColorId = typedArray | |
.getColor(R.styleable.CCLIntroOverlay_ccl_IntroBackgroundColor, | |
Color.argb(0, 0, 0, 0)); | |
} | |
mFocusRadius = builder.mRadius; | |
mListener = builder.mListener; | |
if (mFocusRadius == 0) { | |
mFocusRadius = typedArray | |
.getDimension(R.styleable.CCLIntroOverlay_ccl_IntroFocusRadius, 0); | |
} | |
View view = builder.mView; | |
Rect rect = new Rect(); | |
view.getGlobalVisibleRect(rect); | |
mCenterX = rect.centerX(); | |
mCenterY = rect.centerY(); | |
setFitsSystemWindows(true); | |
setupHolePaint(); | |
setText(builder.mTitleText, builder.mSubtitleText); | |
setButton(builder.mButtonText, typedArray); | |
typedArray.recycle(); | |
} | |
/** | |
* Shows the overlay if it is not visible already. | |
*/ | |
public void show() { | |
if (mIsSingleTime && isFtuShown()) { | |
// we are exceeding the max number | |
mListener = null; | |
return; | |
} | |
if (!mIsOverlayVisible) { | |
mIsOverlayVisible = true; | |
((ViewGroup) ((Activity) getContext()).getWindow().getDecorView()).addView(this); | |
} | |
} | |
/** | |
* Removes the overlay and frees the resources used. It also removes the reference to the | |
* activity that was used to build this overlay to avoid any leaks. When user taps on the | |
* button in this overlay, this method will be called automatically. After calling this method, | |
* for all practical purposes, this component cannot be re-used. | |
*/ | |
public void remove() { | |
if (getContext() != null) { | |
((ViewGroup) ((Activity) getContext()).getWindow().getDecorView()).removeView(this); | |
} | |
if (mBitmap != null && !mBitmap.isRecycled()) { | |
mBitmap.recycle(); | |
} | |
mBitmap = null; | |
mListener = null; | |
} | |
private void setButton(String text, TypedArray typedArray) { | |
String buttonText = text; | |
if (TextUtils.isEmpty(text)) { | |
buttonText = typedArray.getString(R.styleable.CCLIntroOverlay_ccl_IntroButtonText); | |
} | |
int buttonColor = typedArray | |
.getColor(R.styleable.CCLIntroOverlay_ccl_IntroButtonBackgroundColor, | |
Color.argb(0, 0, 0, 0)); | |
mButton.setText(buttonText); | |
mButton.getBackground().setColorFilter(buttonColor, PorterDuff.Mode.MULTIPLY); | |
mButton.setOnClickListener(new OnClickListener() { | |
@Override | |
public void onClick(View view) { | |
fadeOut(FADE_OUT_LENGTH_MS); | |
} | |
}); | |
} | |
private void setText(CharSequence titleText, CharSequence detailText) { | |
if (!TextUtils.isEmpty(titleText)) { | |
mTitleText.setText(titleText); | |
} | |
if (!TextUtils.isEmpty(detailText)) { | |
mSubtitleText.append(detailText); | |
} | |
} | |
private void setupHolePaint() { | |
PorterDuffXfermode xfermode = new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY); | |
mHolePaint = new Paint(); | |
mHolePaint.setColor(0xFFFFFF); | |
mHolePaint.setAlpha(0); | |
mHolePaint.setXfermode(xfermode); | |
mHolePaint.setAntiAlias(true); | |
} | |
@Override | |
protected void dispatchDraw(Canvas canvas) { | |
mBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888); | |
Canvas canvas2 = new Canvas(mBitmap); | |
canvas2.drawColor(mOverlayColorId); | |
canvas2.drawCircle(mCenterX, mCenterY, mFocusRadius, mHolePaint); | |
canvas.drawBitmap(mBitmap, 0, 0, null); | |
super.dispatchDraw(canvas); | |
} | |
@Override | |
public boolean onTouchEvent(MotionEvent event) { | |
return true; | |
} | |
@TargetApi(Build.VERSION_CODES.HONEYCOMB) | |
public void fadeOut(long duration) { | |
ObjectAnimator oa = ObjectAnimator.ofFloat(this, ALPHA_PROPERTY, INVISIBLE_VALUE); | |
oa.setDuration(duration).addListener(new AnimatorListenerAdapter() { | |
@Override | |
public void onAnimationEnd(Animator animator) { | |
setFtuShown(); | |
if(mListener != null) { | |
mListener.onOverlayDismissed(); | |
mListener = null; | |
} | |
remove(); | |
} | |
}); | |
oa.start(); | |
} | |
private void setFtuShown() { | |
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getContext()); | |
sharedPref.edit().putBoolean(FTU_SHOWN_KEY, true).apply(); | |
} | |
private boolean isFtuShown() { | |
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getContext()); | |
return sharedPref.getBoolean(FTU_SHOWN_KEY, false); | |
} | |
/** | |
* The builder class that is used to instantiate an instance of {@link IntroductoryOverlay} | |
*/ | |
public static class Builder { | |
private Context mContext; | |
@ColorRes | |
private float mRadius; | |
private String mButtonText; | |
private String mTitleText; | |
private String mSubtitleText; | |
private int mOverlayColor; | |
private OnOverlayDismissedListener mListener; | |
private boolean mSingleTime; | |
private View mView; | |
/** | |
* The constructor for the Builder class. Note that the context passed here must be an | |
* activity context. | |
*/ | |
public Builder(Context activityContext) { | |
mContext = activityContext; | |
} | |
public IntroductoryOverlay build() { | |
Utils.assertNotNull(mView, "MenuItem or MediaRouteButton"); | |
return new IntroductoryOverlay(this); | |
} | |
/** | |
* Set the {@link MenuItem} referencing the cast button. | |
*/ | |
@TargetApi(Build.VERSION_CODES.HONEYCOMB) | |
public Builder setMenuItem(MenuItem menuItem) { | |
mView = menuItem.getActionView(); | |
return this; | |
} | |
/** | |
* Set the {@link MediaRouteButton} that the ovelay should focus on. | |
*/ | |
public Builder setMediaRouteButton(MediaRouteButton button) { | |
mView = button; | |
return this; | |
} | |
/** | |
* Sets the background color of the overlay view. This will override the value that is | |
* provided in the style resource file. This is optional. | |
*/ | |
public Builder setOverlayColor(@ColorRes int colorId) { | |
mOverlayColor = mContext.getResources().getColor(colorId); | |
return this; | |
} | |
/** | |
* Sets the radius of the "hole" around the cast button. This will override the value | |
* specified in the resource file and is optional. | |
*/ | |
public Builder setFocusRadiusId(@DimenRes int radiusId) { | |
mRadius = mContext.getResources().getDimension(radiusId); | |
return this; | |
} | |
/** | |
* Sets the radius of the "hole" around the cast button. This will override the value | |
* specified in the resource file and is optional. | |
*/ | |
public Builder setFocusRadius(float radius) { | |
mRadius = radius; | |
return this; | |
} | |
/** | |
* Sets the text used in the confirmation button on this overlay. This will override the | |
* value specified in the resource file and is optional. | |
*/ | |
public Builder setButtonText(String text) { | |
mButtonText = text; | |
return this; | |
} | |
/** | |
* Sets the text used in the confirmation button on this overlay. This will override the | |
* value specified in the resource file and is optional. | |
*/ | |
public Builder setButtonText(@StringRes int stringId) { | |
mButtonText = mContext.getResources().getString(stringId); | |
return this; | |
} | |
/** | |
* Sets the text used in the Title area on the overlay. This will override the | |
* value specified in the resource file and is optional. | |
*/ | |
public Builder setTitleText(@StringRes int stringId) { | |
mTitleText = mContext.getResources().getString(stringId); | |
return this; | |
} | |
/** | |
* Sets the text used in the Title area on the overlay. This will override the | |
* value specified in the resource file and is optional. | |
*/ | |
public Builder setTitleText(String text) { | |
mTitleText = text; | |
return this; | |
} | |
/** | |
* Sets the text used in the Subtitle area on the overlay. This is optional. | |
*/ | |
public Builder setSubtitleText(@StringRes int stringId) { | |
mSubtitleText = mContext.getResources().getString(stringId); | |
return this; | |
} | |
/** | |
* Sets the text used in the Subtitle area on the overlay. This is optional. | |
*/ | |
public Builder setSubtitleText(String text) { | |
mSubtitleText = text; | |
return this; | |
} | |
/** | |
* Sets an {@link OnOverlayDismissedListener} listener that will be notified when the | |
* overlay is dismissed by pressing on the confirmation button. | |
*/ | |
public Builder setOnDismissed(OnOverlayDismissedListener listener) { | |
mListener = listener; | |
return this; | |
} | |
/** | |
* Sets the maximum number of times that this overlay should be shown to 1. If it is needed | |
* to show this more than once, management of the counter has to be done by the client. | |
*/ | |
public Builder setSingleTime() { | |
mSingleTime = true; | |
return this; | |
} | |
} | |
/** | |
* An interface to notify the clients when the ovelay is dismissed explicitly when user taps | |
* on the confirmation button. | |
*/ | |
public interface OnOverlayDismissedListener { | |
void onOverlayDismissed(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment