/* * This is the source code of Telegram for Android v. 1.4.x. * It is licensed under GNU GPL v. 2 or later. * You should have received a copy of the license in this archive (see LICENSE). * * Copyright Nikolai Kudashov, 2013-2014. */ package org.telegram.ui.Views.ActionBar; import android.animation.Animator; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.res.Configuration; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.view.ActionMode; import android.view.Gravity; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.view.Window; import android.view.WindowManager; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.widget.FrameLayout; import org.telegram.android.AndroidUtilities; import org.telegram.messenger.FileLog; import org.telegram.messenger.R; import org.telegram.messenger.Utilities; import java.util.ArrayList; public class ActionBarActivity extends Activity { private class FrameLayoutAnimationListener extends FrameLayout { public FrameLayoutAnimationListener(Context context) { super(context); } @Override protected void onAnimationEnd() { super.onAnimationEnd(); ActionBarActivity.this.onAnimationEnd(); } } protected ActionBar actionBar; private FrameLayoutAnimationListener containerView; private FrameLayoutAnimationListener containerViewBack; protected FrameLayout contentView; private View shadowView; private Animation openAnimation; private Animation closeAnimation; private boolean maybeStartTracking = false; protected boolean startedTracking = false; private int startedTrackingX; private int startedTrackingY; protected boolean animationInProgress = false; private VelocityTracker velocityTracker = null; private boolean beginTrackingSent = false; private boolean transitionAnimationInProgress = false; private long transitionAnimationStartTime; private boolean inActionMode = false; private int startedTrackingPointerId; private Runnable onCloseAnimationEndRunnable = null; private Runnable onOpenAnimationEndRunnable = null; private class FrameLayoutTouch extends FrameLayout { public FrameLayoutTouch(Context context) { super(context); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { return !(!animationInProgress && !checkTransitionAnimation()) || ((ActionBarActivity) getContext()).onTouchEvent(ev); } @Override public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { ((ActionBarActivity)getContext()).onTouchEvent(null); super.requestDisallowInterceptTouchEvent(disallowIntercept); } @Override public boolean dispatchKeyEventPreIme(KeyEvent event) { if (event != null && event.getKeyCode() == KeyEvent.KEYCODE_BACK) { return ((ActionBarActivity)getContext()).onPreIme() || super.dispatchKeyEventPreIme(event); } return super.dispatchKeyEventPreIme(event); } @Override public boolean onKeyPreIme(int keyCode, KeyEvent event) { return super.onKeyPreIme(keyCode, event); } } public static ArrayList fragmentsStack = new ArrayList(); protected void onCreateFinish(Bundle savedInstanceState) { super.onCreate(savedInstanceState); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); try { openAnimation = AnimationUtils.loadAnimation(this, R.anim.scale_in); closeAnimation = AnimationUtils.loadAnimation(this, R.anim.scale_out); } catch (Exception e) { FileLog.e("tmessages", e); } setTheme(R.style.Theme_TMessages); getWindow().setBackgroundDrawableResource(R.drawable.transparent); contentView = new FrameLayoutTouch(this); setContentView(contentView, new ViewGroup.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)); containerViewBack = new FrameLayoutAnimationListener(this); contentView.addView(containerViewBack); containerView = new FrameLayoutAnimationListener(this); contentView.addView(containerView); shadowView = new FrameLayout(this); contentView.addView(shadowView); shadowView.setBackgroundResource(R.drawable.shadow); ViewGroup.LayoutParams layoutParams = shadowView.getLayoutParams(); layoutParams.width = AndroidUtilities.dp(2); layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; shadowView.setLayoutParams(layoutParams); shadowView.setVisibility(View.INVISIBLE); actionBar = new ActionBar(this); contentView.addView(actionBar); layoutParams = actionBar.getLayoutParams(); layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; actionBar.setLayoutParams(layoutParams); for (BaseFragment fragment : fragmentsStack) { fragment.setParentActivity(this); } needLayout(); } @Override public void onConfigurationChanged(android.content.res.Configuration newConfig) { super.onConfigurationChanged(newConfig); fixLayout(); if (!fragmentsStack.isEmpty()) { BaseFragment lastFragment = fragmentsStack.get(fragmentsStack.size() - 1); lastFragment.onConfigurationChanged(newConfig); } } @Override protected void onResume() { super.onResume(); fixLayout(); if (transitionAnimationInProgress) { if (onCloseAnimationEndRunnable != null) { closeAnimation.cancel(); onCloseAnimationEnd(false); } else if (onOpenAnimationEndRunnable != null) { openAnimation.cancel(); onOpenAnimationEnd(false); } } if (!fragmentsStack.isEmpty()) { BaseFragment lastFragment = fragmentsStack.get(fragmentsStack.size() - 1); lastFragment.onResume(); actionBar.setCurrentActionBarLayer(lastFragment.actionBarLayer); } } @Override protected void onPause() { super.onPause(); if (!fragmentsStack.isEmpty()) { BaseFragment lastFragment = fragmentsStack.get(fragmentsStack.size() - 1); lastFragment.onPause(); } } private void onSlideAnimationEnd(boolean backAnimation) { containerView.setX(0); containerViewBack.setX(0); actionBar.stopMoving(backAnimation); shadowView.setVisibility(View.INVISIBLE); shadowView.setX(-AndroidUtilities.dp(2)); if (!backAnimation) { BaseFragment lastFragment = fragmentsStack.get(fragmentsStack.size() - 1); lastFragment.onPause(); lastFragment.onFragmentDestroy(); lastFragment.setParentActivity(null); fragmentsStack.remove(fragmentsStack.size() - 1); FrameLayoutAnimationListener temp = containerView; containerView = containerViewBack; containerViewBack = temp; ViewGroup parent = (ViewGroup)containerView.getParent(); parent.removeView(containerView); parent.addView(containerView, 1); lastFragment = fragmentsStack.get(fragmentsStack.size() - 1); lastFragment.onResume(); } else { BaseFragment lastFragment = fragmentsStack.get(fragmentsStack.size() - 2); lastFragment.onPause(); if (lastFragment.fragmentView != null) { ViewGroup parent = (ViewGroup) lastFragment.fragmentView.getParent(); if (parent != null) { parent.removeView(lastFragment.fragmentView); } } } containerViewBack.setVisibility(View.GONE); AndroidUtilities.unlockOrientation(this); startedTracking = false; animationInProgress = false; } private void prepareForMoving(MotionEvent ev) { maybeStartTracking = false; startedTracking = true; startedTrackingX = (int) ev.getX(); shadowView.setVisibility(View.VISIBLE); shadowView.setX(-AndroidUtilities.dp(2)); containerViewBack.setVisibility(View.VISIBLE); beginTrackingSent = false; BaseFragment lastFragment = fragmentsStack.get(fragmentsStack.size() - 2); actionBar.prepareForMoving(lastFragment.actionBarLayer); View fragmentView = lastFragment.createView(getLayoutInflater(), null); ViewGroup parentView = (ViewGroup)fragmentView.getParent(); if (parentView != null) { parentView.removeView(fragmentView); } containerViewBack.addView(fragmentView); ViewGroup.LayoutParams layoutParams = fragmentView.getLayoutParams(); layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; fragmentView.setLayoutParams(layoutParams); if (fragmentView.getBackground() == null) { fragmentView.setBackgroundColor(0xffffffff); } lastFragment.onResume(); AndroidUtilities.lockOrientation(this); } public boolean onTouchEvent(MotionEvent ev) { if(android.os.Build.VERSION.SDK_INT >= 11 && !checkTransitionAnimation() && !inActionMode && fragmentsStack.size() > 1 && !animationInProgress) { if (ev != null && ev.getAction() == MotionEvent.ACTION_DOWN && !startedTracking && !maybeStartTracking) { BaseFragment currentFragment = fragmentsStack.get(fragmentsStack.size() - 1); if (!currentFragment.swipeBackEnabled) { return false; } startedTrackingPointerId = ev.getPointerId(0); maybeStartTracking = true; startedTrackingX = (int) ev.getX(); startedTrackingY = (int) ev.getY(); if (velocityTracker != null) { velocityTracker.clear(); } } else if (ev != null && ev.getAction() == MotionEvent.ACTION_MOVE && ev.getPointerId(0) == startedTrackingPointerId) { if (velocityTracker == null) { velocityTracker = VelocityTracker.obtain(); } int dx = Math.max(0, (int) (ev.getX() - startedTrackingX)); int dy = Math.abs((int)ev.getY() - startedTrackingY); velocityTracker.addMovement(ev); if (maybeStartTracking && !startedTracking && dx >= AndroidUtilities.dp(10) && Math.abs(dx) / 3 > dy) { prepareForMoving(ev); } else if (startedTracking) { if (!beginTrackingSent) { if (getCurrentFocus() != null) { AndroidUtilities.hideKeyboard(getCurrentFocus()); } BaseFragment currentFragment = fragmentsStack.get(fragmentsStack.size() - 1); currentFragment.onBeginSlide(); beginTrackingSent = true; } actionBar.moveActionBarByX(dx); containerView.setX(dx); shadowView.setX(dx - AndroidUtilities.dp(2)); } } else if (ev != null && ev.getPointerId(0) == startedTrackingPointerId && (ev.getAction() == MotionEvent.ACTION_CANCEL || ev.getAction() == MotionEvent.ACTION_UP || ev.getAction() == MotionEvent.ACTION_POINTER_UP)) { if (velocityTracker == null) { velocityTracker = VelocityTracker.obtain(); } velocityTracker.computeCurrentVelocity(1000); if (!startedTracking) { float velX = velocityTracker.getXVelocity(); float velY = velocityTracker.getYVelocity(); if (velX >= 3500 && velX > velY) { prepareForMoving(ev); } } if (startedTracking) { float x = containerView.getX(); ArrayList animators = new ArrayList(); float velX = velocityTracker.getXVelocity(); float velY = velocityTracker.getYVelocity(); final boolean backAnimation = x < containerView.getMeasuredWidth() / 3.0f && (velX < 3500 || velX < velY); float distToMove = 0; if (!backAnimation) { distToMove = containerView.getMeasuredWidth() - x; animators.add(ObjectAnimator.ofFloat(containerView, "x", containerView.getMeasuredWidth())); animators.add(ObjectAnimator.ofFloat(shadowView, "x", containerView.getMeasuredWidth() - AndroidUtilities.dp(2))); } else { distToMove = x; animators.add(ObjectAnimator.ofFloat(containerView, "x", 0)); animators.add(ObjectAnimator.ofFloat(shadowView, "x", -AndroidUtilities.dp(2))); } actionBar.setupAnimations(animators, backAnimation); AnimatorSet animatorSet = new AnimatorSet(); animatorSet.playTogether(animators); animatorSet.setDuration(Math.max((int) (200.0f / containerView.getMeasuredWidth() * distToMove), 50)); animatorSet.start(); animationInProgress = true; animatorSet.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animator) { } @Override public void onAnimationEnd(Animator animator) { onSlideAnimationEnd(backAnimation); } @Override public void onAnimationCancel(Animator animator) { onSlideAnimationEnd(backAnimation); } @Override public void onAnimationRepeat(Animator animator) { } }); } else { maybeStartTracking = false; startedTracking = false; } if (velocityTracker != null) { velocityTracker.recycle(); velocityTracker = null; } } else if (ev == null) { maybeStartTracking = false; startedTracking = false; if (velocityTracker != null) { velocityTracker.recycle(); velocityTracker = null; } } return startedTracking; } return false; } @Override public void onBackPressed() { if (startedTracking || checkTransitionAnimation() || fragmentsStack.isEmpty()) { return; } if (actionBar.currentLayer != null && actionBar.currentLayer.isSearchFieldVisible) { actionBar.currentLayer.closeSearchField(); return; } BaseFragment lastFragment = fragmentsStack.get(fragmentsStack.size() - 1); if (lastFragment.onBackPressed()) { if (fragmentsStack.size() == 1) { fragmentsStack.get(0).onFragmentDestroy(); fragmentsStack.clear(); onFinish(); finish(); } else if (!fragmentsStack.isEmpty()) { closeLastFragment(true); } } } @Override public void onLowMemory() { super.onLowMemory(); for (BaseFragment fragment : fragmentsStack) { fragment.onLowMemory(); } } public boolean checkTransitionAnimation() { if (transitionAnimationInProgress && transitionAnimationStartTime < System.currentTimeMillis() - 400) { transitionAnimationInProgress = false; onAnimationEnd(); } return transitionAnimationInProgress; } private void fixLayout() { if (contentView != null) { ViewTreeObserver obs = contentView.getViewTreeObserver(); obs.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { needLayout(); if (Build.VERSION.SDK_INT < 16) { contentView.getViewTreeObserver().removeGlobalOnLayoutListener(this); } else { contentView.getViewTreeObserver().removeOnGlobalLayoutListener(this); } } }); } } public void needLayout() { WindowManager manager = (WindowManager) getSystemService(WINDOW_SERVICE); int rotation = manager.getDefaultDisplay().getRotation(); int height = 0; if (actionBar.getVisibility() == View.VISIBLE) { if (!Utilities.isTablet(this) && getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { height = AndroidUtilities.dp(40); } else { height = AndroidUtilities.dp(48); } } if (containerView != null) { FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) containerView.getLayoutParams(); if (layoutParams.topMargin != height) { layoutParams.setMargins(0, height, 0, 0); layoutParams.gravity = Gravity.TOP | Gravity.LEFT; containerView.setLayoutParams(layoutParams); } } if (containerViewBack != null) { FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) containerViewBack.getLayoutParams(); if (layoutParams.topMargin != height) { layoutParams.setMargins(0, height, 0, 0); layoutParams.gravity = Gravity.TOP | Gravity.LEFT; containerViewBack.setLayoutParams(layoutParams); } } } public ActionBar getInternalActionBar() { return actionBar; } private void presentFragmentInternalRemoveOld(boolean removeLast, final BaseFragment fragment) { if (fragment == null) { return; } fragment.onPause(); if (removeLast) { fragment.onFragmentDestroy(); fragment.setParentActivity(null); fragmentsStack.remove(fragment); } else { if (fragment.fragmentView != null) { ViewGroup parent = (ViewGroup) fragment.fragmentView.getParent(); if (parent != null) { parent.removeView(fragment.fragmentView); } } } containerViewBack.setVisibility(View.GONE); } public boolean presentFragment(BaseFragment fragment) { return presentFragment(fragment, false, false); } public boolean presentFragment(BaseFragment fragment, boolean removeLast) { return presentFragment(fragment, removeLast, false); } public boolean presentFragment(final BaseFragment fragment, final boolean removeLast, boolean forceWithoutAnimation) { if (checkTransitionAnimation() || !fragment.onFragmentCreate()) { return false; } if (getCurrentFocus() != null) { AndroidUtilities.hideKeyboard(getCurrentFocus()); } boolean needAnimation = openAnimation != null && !forceWithoutAnimation && getSharedPreferences("mainconfig", Activity.MODE_PRIVATE).getBoolean("view_animations", true); final BaseFragment currentFragment = !fragmentsStack.isEmpty() ? fragmentsStack.get(fragmentsStack.size() - 1) : null; fragment.setParentActivity(this); View fragmentView = fragment.createView(getLayoutInflater(), null); containerViewBack.addView(fragmentView); ViewGroup.LayoutParams layoutParams = fragmentView.getLayoutParams(); layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; fragmentView.setLayoutParams(layoutParams); fragmentsStack.add(fragment); fragment.onResume(); actionBar.setCurrentActionBarLayer(fragment.actionBarLayer); if (fragmentView.getBackground() == null) { fragmentView.setBackgroundColor(0xffffffff); } FrameLayoutAnimationListener temp = containerView; containerView = containerViewBack; containerViewBack = temp; containerView.setVisibility(View.VISIBLE); ViewGroup parent = (ViewGroup)containerView.getParent(); parent.removeView(containerView); parent.addView(containerView, 1); if (!needAnimation) { presentFragmentInternalRemoveOld(removeLast, currentFragment); } if (needAnimation) { transitionAnimationStartTime = System.currentTimeMillis(); transitionAnimationInProgress = true; onOpenAnimationEndRunnable = new Runnable() { @Override public void run() { presentFragmentInternalRemoveOld(removeLast, currentFragment); fragment.onOpenAnimationEnd(); } }; openAnimation.reset(); containerView.startAnimation(openAnimation); } else { fragment.onOpenAnimationEnd(); } return true; } public boolean addFragmentToStack(BaseFragment fragment) { if (!fragment.onFragmentCreate()) { return false; } fragment.setParentActivity(this); fragmentsStack.add(fragment); return true; } private void closeLastFragmentInternalRemoveOld(BaseFragment fragment) { fragment.onPause(); fragment.onFragmentDestroy(); fragment.setParentActivity(null); fragmentsStack.remove(fragment); containerViewBack.setVisibility(View.GONE); ViewGroup parent = (ViewGroup)containerView.getParent(); parent.removeView(containerViewBack); parent.addView(containerViewBack, 0); } public void closeLastFragment(boolean animated) { if (fragmentsStack.size() <= 1 || checkTransitionAnimation()) { return; } if (getCurrentFocus() != null) { AndroidUtilities.hideKeyboard(getCurrentFocus()); } boolean needAnimation = animated && closeAnimation != null && getSharedPreferences("mainconfig", Activity.MODE_PRIVATE).getBoolean("view_animations", true); final BaseFragment currentFragment = fragmentsStack.get(fragmentsStack.size() - 1); BaseFragment previousFragment = fragmentsStack.get(fragmentsStack.size() - 2); FrameLayoutAnimationListener temp = containerView; containerView = containerViewBack; containerViewBack = temp; containerView.setVisibility(View.VISIBLE); previousFragment.setParentActivity(this); View fragmentView = previousFragment.createView(getLayoutInflater(), null); containerView.addView(fragmentView); ViewGroup.LayoutParams layoutParams = fragmentView.getLayoutParams(); layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; fragmentView.setLayoutParams(layoutParams); previousFragment.onResume(); actionBar.setCurrentActionBarLayer(previousFragment.actionBarLayer); if (fragmentView.getBackground() == null) { fragmentView.setBackgroundColor(0xffffffff); } if (!needAnimation) { closeLastFragmentInternalRemoveOld(currentFragment); } if (needAnimation) { transitionAnimationStartTime = System.currentTimeMillis(); transitionAnimationInProgress = true; closeAnimation.reset(); onCloseAnimationEndRunnable = new Runnable() { @Override public void run() { closeLastFragmentInternalRemoveOld(currentFragment); } }; containerViewBack.startAnimation(closeAnimation); } } public void showLastFragment() { BaseFragment previousFragment = fragmentsStack.get(fragmentsStack.size() - 1); previousFragment.setParentActivity(this); View fragmentView = previousFragment.createView(getLayoutInflater(), null); containerView.addView(fragmentView); ViewGroup.LayoutParams layoutParams = fragmentView.getLayoutParams(); layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT; layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT; fragmentView.setLayoutParams(layoutParams); previousFragment.onResume(); actionBar.setCurrentActionBarLayer(previousFragment.actionBarLayer); if (fragmentView.getBackground() == null) { fragmentView.setBackgroundColor(0xffffffff); } } public void removeFragmentFromStack(BaseFragment fragment) { fragment.onPause(); fragment.onFragmentDestroy(); fragment.setParentActivity(null); fragmentsStack.remove(fragment); } public void rebuildAllFragmentViews() { for (int a = 0; a < fragmentsStack.size() - 1; a++) { fragmentsStack.get(a).setParentActivity(null); fragmentsStack.get(a).setParentActivity(this); } } protected void onFinish() { } public void showActionBar() { actionBar.setVisibility(View.VISIBLE); needLayout(); } public void hideActionBar() { actionBar.setVisibility(View.GONE); needLayout(); } public boolean onKeyUp(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_MENU && !checkTransitionAnimation() && !startedTracking) { actionBar.onMenuButtonPressed(); } return super.onKeyUp(keyCode, event); } public void onOverlayShow(View view, BaseFragment fragment) { } @Override public void onActionModeStarted(ActionMode mode) { super.onActionModeStarted(mode); hideActionBar(); inActionMode = true; } @Override public void onActionModeFinished(ActionMode mode) { super.onActionModeFinished(mode); showActionBar(); inActionMode = false; } public boolean onPreIme() { return false; } private void onCloseAnimationEnd(boolean post) { if (transitionAnimationInProgress && onCloseAnimationEndRunnable != null) { transitionAnimationInProgress = false; transitionAnimationStartTime = 0; if (post) { new Handler().post(new Runnable() { public void run() { onCloseAnimationEndRunnable.run(); onCloseAnimationEndRunnable = null; } }); } else { onCloseAnimationEndRunnable.run(); onCloseAnimationEndRunnable = null; } } } private void onOpenAnimationEnd(boolean post) { if (transitionAnimationInProgress && onOpenAnimationEndRunnable != null) { transitionAnimationInProgress = false; transitionAnimationStartTime = 0; if (post) { new Handler().post(new Runnable() { public void run() { onOpenAnimationEndRunnable.run(); onOpenAnimationEndRunnable = null; } }); } else { onOpenAnimationEndRunnable.run(); onOpenAnimationEndRunnable = null; } } } private void onAnimationEnd() { onCloseAnimationEnd(false); onOpenAnimationEnd(false); } @Override public void startActivityForResult(final Intent intent, final int requestCode) { if (transitionAnimationInProgress) { if (onCloseAnimationEndRunnable != null) { closeAnimation.cancel(); onCloseAnimationEnd(false); } else if (onOpenAnimationEndRunnable != null) { openAnimation.cancel(); onOpenAnimationEnd(false); } containerView.invalidate(); if (intent != null) { try { ActionBarActivity.super.startActivityForResult(intent, requestCode); } catch (Exception e) { FileLog.e("tmessages", e); } } } else { if (intent != null) { try { super.startActivityForResult(intent, requestCode); } catch (Exception e) { FileLog.e("tmessages", e); } } } } }