package com.kaichunlin.transition;
import android.app.Activity;
import android.support.annotation.CheckResult;
import android.support.annotation.IdRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.widget.Toolbar;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.ImageView;
import com.kaichunlin.transition.internal.TransitionController;
import com.kaichunlin.transition.internal.TransitionControllerManager;
import com.kaichunlin.transition.util.TransitionStateLogger;
import com.kaichunlin.transition.util.TransitionUtil;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
/**
* Provides transition effects to a MenuItem.
*/
public class MenuItemTransition extends AbstractTransition<MenuItemTransition, MenuItemTransition.Setup> {
private List<TransitionControllerManager> mTransittingMenuItems = new ArrayList<>();
private final Toolbar mToolbar;
private boolean mStarted;
private boolean mSetVisibleOnStartTransition;
private boolean mInvalidateOptionOnStopTransition;
private WeakReference<Activity> mActivityRef;
private int mMenuId;
private List<MenuItem> menuItemList;
/**
* Creates transition effect for a Toolbar's {@link MenuItem}s.
*
* @param toolbar
* @return
*/
public MenuItemTransition(@NonNull Toolbar toolbar) {
this(null, toolbar, null);
}
/**
* Creates transition effect for a Toolbar's {@link MenuItem}s.
*
* @param id Same as calling {@link #setId(String)}, set an ID for debugging purpose.
* @param toolbar
* @return
*/
public MenuItemTransition(@Nullable String id, @NonNull Toolbar toolbar) {
this(id, toolbar, null);
}
/**
* Creates transition effect for a Toolbar's {@link MenuItem}s.
*
* @param toolbar
* @param view The custom View to display when the transition is active.
* @return
*/
public MenuItemTransition(@NonNull Toolbar toolbar, @Nullable View view) {
this(null, toolbar, view);
}
/**
* Creates transition effect for a Toolbar's {@link MenuItem}s.
*
* @param id Same as calling {@link #setId(String)}, set an ID for debugging purpose.
* @param toolbar
* @param view The custom View to display when the transition is active.
* @return
*/
public MenuItemTransition(@Nullable String id, @NonNull Toolbar toolbar, @Nullable View view) {
super(id);
this.mToolbar = toolbar;
this.mTarget = view;
}
public MenuItemTransition setMenuId(@IdRes int menuId) {
mMenuId = menuId;
return self();
}
public int getMenuId() {
return mMenuId;
}
/**
* @param visible Sets whether the visibility of the target MenuItem be forcibly set when
* {@link #startTransition()} is called
* @return
*/
public MenuItemTransition setVisibleOnStartAnimation(boolean visible) {
this.mSetVisibleOnStartTransition = visible;
return self();
}
@Override
public MenuItemTransition reverse() {
super.reverse();
mTransittingMenuItems.clear();
return self();
}
@Override
public boolean startTransition() {
if (mStarted) {
return false;
}
if (mSetVisibleOnStartTransition) {
mToolbar.getMenu().setGroupVisible(0, true);
}
//only retrieve MenuItem from mToolbar when run for the first time
if (mTransittingMenuItems.size() == 0) {
if (mMenuId == 0) {
menuItemList = TransitionUtil.getVisibleMenuItemList(mToolbar);
} else { //this transition only applies to a specific MenuItem
menuItemList = new ArrayList<>();
MenuItem menuItem = TransitionUtil.getMenuItem(mToolbar, mMenuId);
if (menuItem == null) {
return false;
}
menuItemList.add(menuItem);
}
LayoutInflater layoutInflater = LayoutInflater.from(mToolbar.getContext());
int j;
int setupSize;
for (int i = 0, size = menuItemList.size(); i < size; i++) {
MenuItem menuItem = menuItemList.get(i);
TransitionControllerManager transitionControllerManager = new TransitionControllerManager(getId());
if (mInterpolator != null) {
transitionControllerManager.setInterpolator(mInterpolator);
}
setupSize = mSetupList.size();
for (j = 0; j < setupSize; j++) {
mSetupList.get(j).setupAnimation(menuItem, transitionControllerManager, i, size);
}
View view;
if (mTarget == null) { //uses default view with original menu icon
view = layoutInflater.inflate(R.layout.menu_animation, null).findViewById(R.id.menu_animation);
((ImageView) view).setImageDrawable(menuItem.getIcon());
} else {
view = mTarget;
}
if (TransitionConfig.isDebug()) {
view.setTag(R.id.debug_id, new TransitionStateLogger(getId()));
}
if (mReverse) {
transitionControllerManager.reverse();
}
menuItem.setActionView(view);
transitionControllerManager.setTarget(view);
transitionControllerManager.start();
mTransittingMenuItems.add(transitionControllerManager);
}
}
mStarted = true;
return true;
}
@Override
public void updateProgress(float progress) {
for (int i = 0, size = mTransittingMenuItems.size(); i < size; i++) {
mTransittingMenuItems.get(i).updateProgress(progress);
}
}
@Override
public void stopTransition() {
for (int i = 0, size = mTransittingMenuItems.size(); i < size; i++) {
mTransittingMenuItems.get(i).end();
}
if (menuItemList == null) {
return;
}
for (int i = 0, size = menuItemList.size(); i < size; i++) {
menuItemList.get(i).setActionView(null);
}
mTransittingMenuItems.clear();
if (mInvalidateOptionOnStopTransition) {
Activity activity = getActivity();
if (activity != null) {
activity.invalidateOptionsMenu();
}
}
mStarted = false;
}
private Activity getActivity() {
return mActivityRef == null ? null : mActivityRef.get();
}
/**
* @return Should {@link Activity#invalidateOptionsMenu()} be called when transition stops.
*/
public boolean isInvalidateOptionOnStopTransition() {
return mInvalidateOptionOnStopTransition;
}
/**
* Sets whether or not to call {@link Activity#invalidateOptionsMenu()} after a transition stops.
*
* @param activity Activity that should have its {@link Activity#invalidateOptionsMenu()} method
* called, or null if invalidate parameter is false.
* @param invalidate
* @return
*/
public MenuItemTransition setInvalidateOptionOnStopTransition(@NonNull Activity activity, boolean invalidate) {
this.mActivityRef = new WeakReference<>(activity);
this.mInvalidateOptionOnStopTransition = invalidate;
return self();
}
@CheckResult
@Override
public MenuItemTransition clone() {
MenuItemTransition newCopy = (MenuItemTransition) super.clone();
newCopy.mTransittingMenuItems = new ArrayList<>(mTransittingMenuItems.size());
for (int i = 0, size = mTransittingMenuItems.size(); i < size; i++) {
newCopy.mTransittingMenuItems.add(mTransittingMenuItems.get(i).clone());
}
return newCopy;
}
@Override
protected void invalidate() {
}
@Override
public boolean isCompatible(AbstractTransition another) {
if (super.isCompatible(another) && mMenuId == ((MenuItemTransition) another).mMenuId) {
return true;
}
return false;
}
@Override
public boolean merge(AbstractTransition another) {
if (super.merge(another)) {
MenuItemTransition mit = (MenuItemTransition) another;
mSetVisibleOnStartTransition |= mit.mSetVisibleOnStartTransition;
mInvalidateOptionOnStopTransition |= mit.mInvalidateOptionOnStopTransition;
return true;
}
return false;
}
@Override
protected MenuItemTransition self() {
return this;
}
/**
* Represents an object that will create {@link TransitionController}s to be added to a
* {@link TransitionControllerManager}.
*/
public interface Setup extends AbstractTransition.Setup {
/**
* Create one or more {@link TransitionController} for each {@link android.view.MenuItem} and add
* them to TransitionControllerManager.
*
* @param mMenuItem
* @param manager
* @param itemIndex Position of the MenuItem.
* @param menuCount Total number of MenuItem's.
*/
void setupAnimation(MenuItem mMenuItem, TransitionControllerManager manager, int itemIndex, int menuCount);
}
}