/*
* Copyright 2015. Appsi Mobile
*
* 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.appsimobile.appsii;
import android.animation.ObjectAnimator;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.util.CircularArray;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import com.appsimobile.appsii.dagger.AppsiInjector;
import javax.inject.Inject;
/**
* This class is the basic implementation of the sidebar. It communicates various
* state events from Appsii to the SidebarPagerAdapter and manages the ViewPager
*/
public class Sidebar extends RelativeLayout
implements LoaderListener, View.OnClickListener,
SharedPreferences.OnSharedPreferenceChangeListener,
AbstractSidebarPagerAdapter.FlagListener {
/** The delay before the sidebar is closed after an app-widget action was clicked */
static final int DELAYED_CLOSE_DURATION = 2500;
/**
* The delay before the sidebar is closed after an app-widget action was clicked
* that needs user feedback
*/
static final int DELAYED_CLOSE_DURATION_WHEN_ASKED = 8000;
/**
* A bundle used to manage state in by any client after an app-widget close
* message is being shown
*/
final Bundle mCloseStateBundle = new Bundle();
private final OnPageChangeListenerImpl mOnPageChangeListener = new OnPageChangeListenerImpl();
/**
* A listener that can be called when the sidebar needs to be closed
*/
SidebarListener mSidebarListener;
/**
* True when we are opening from the left side. False otherwise.
* Left and right are absolute and also used in rtl locales
*/
boolean mIsLeft;
View mSidebarBackgroundView;
View mLeftShadow;
View mRightShadow;
OnCancelCloseListener mOnCancelCloseListener;
View mCancelClosingOverlay;
ProgressBar mCancelClosingProgressBar;
ObjectAnimator mClosingProgressBarAnimator;
View mCloseText;
View mCancelClosingOverlayAlwaysCloseButton;
View mCancelClosingOverlayNeverCloseButton;
@Inject
SidebarContext mAppsiContext;
@Inject
SharedPreferences mSharedPreferences;
private ViewPager mAppsiViewPager;
private SidebarPagerAdapter mAdapter;
public Sidebar(Context context) {
super(context);
init();
}
public Sidebar(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public Sidebar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private static int indexOfId(CircularArray<HotspotPageEntry> entries, long defaultPage) {
if (defaultPage == -1L) return -1;
for (int i = 0; i < entries.size(); i++) {
HotspotPageEntry e = entries.get(i);
if (e.mPageId == defaultPage) return i;
}
return -1;
}
private void init() {
AppsiInjector.inject(this);
}
public void onTrimMemory(int level) {
if (mAdapter != null) {
mAdapter.onTrimMemory(level);
}
}
public void setOnCancelCloseListener(OnCancelCloseListener onCancelCloseListener) {
mOnCancelCloseListener = onCancelCloseListener;
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
switch (key) {
case "pref_paging_animation":
setupPagingAnimation(sharedPreferences);
break;
}
}
private void setupPagingAnimation(SharedPreferences sharedPreferences) {
String pagingAnimation =
sharedPreferences.getString("pref_paging_animation", "fade");
updatePagingAnimation(pagingAnimation);
}
private void updatePagingAnimation(String pagingAnimation) {
if (pagingAnimation == null) return;
switch (pagingAnimation) {
case "fade":
mAppsiViewPager.setPageTransformer(true, new FadePageTransformer());
break;
case "depth":
mAppsiViewPager.setPageTransformer(true, new DepthPageTransformer());
break;
case "zoom":
mAppsiViewPager.setPageTransformer(true, new ZoomOutPageTransformer());
break;
case "tablet":
mAppsiViewPager.setPageTransformer(true, new TabletPageTransformer());
break;
}
}
@Override
public boolean onKeyDown(int keyCode, @NonNull KeyEvent event) {
return super.onKeyDown(keyCode, event);
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
closeSidebar();
return true;
}
return super.onKeyUp(keyCode, event);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mAppsiContext.setContentWidth(w);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
// we need to find the actionbar before we initialize the view
// pager adapter. All views inside the adapter will assume a
// special SidebarContext that provides access to the actionbar.
mAppsiViewPager = (ViewPager) findViewById(R.id.appsi_view_pager);
mAppsiViewPager.addOnPageChangeListener(mOnPageChangeListener);
mAdapter = new SidebarPagerAdapter(mAppsiContext);
mAppsiViewPager.setAdapter(mAdapter);
mAdapter.setFlagListener(this);
mSidebarBackgroundView = findViewById(R.id.sidebar_back);
mLeftShadow = findViewById(R.id.sidebar_left_shadow);
mRightShadow = findViewById(R.id.sidebar_right_shadow);
mCloseText = findViewById(R.id.always_close_text);
mCancelClosingOverlayAlwaysCloseButton = findViewById(R.id.always_close);
mCancelClosingOverlayNeverCloseButton = findViewById(R.id.never_close);
mCancelClosingOverlayAlwaysCloseButton.
setBackground(ThemingUtils.createButtonDrawable(mAppsiContext));
mCancelClosingOverlayNeverCloseButton.
setBackground(ThemingUtils.createButtonDrawable(mAppsiContext));
mCancelClosingOverlay = findViewById(R.id.widget_action_overlay);
mCancelClosingProgressBar = (ProgressBar) findViewById(R.id.closing_progress_bar);
mCancelClosingOverlay.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
cancelClosingOverlay();
return true;
}
});
mClosingProgressBarAnimator =
ObjectAnimator.ofInt(mCancelClosingProgressBar, "progress", 100, 0);
mClosingProgressBarAnimator.setDuration(DELAYED_CLOSE_DURATION);
setFocusableInTouchMode(true);
if (!isInEditMode()) {
findViewById(R.id.sidebar_close_area).setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
closeSidebar();
return true;
}
});
}
mCancelClosingOverlayAlwaysCloseButton.setOnClickListener(this);
mCancelClosingOverlayNeverCloseButton.setOnClickListener(this);
}
protected void cancelClosingOverlay() {
mOnCancelCloseListener.onCloseCancelled();
mCancelClosingOverlay.animate().alpha(0);
mCancelClosingOverlay.setVisibility(View.GONE);
}
boolean closeSidebar() {
// mSearchBox.setText("");
if (mSidebarListener != null) {
mSidebarListener.onCloseSidebar();
return true;
}
return false;
}
public void setSidebarListener(SidebarListener sidebarListener) {
mSidebarListener = sidebarListener;
}
@Override
public void onClick(View view) {
int id = view.getId();
if (id == R.id.always_close) {
mAdapter.rememberCloseAction(mCloseStateBundle,
SidebarPagerAdapter.CLOSE_ACTION_AUTO_CLOSE);
mSidebarListener.onCloseSidebar();
} else if (id == R.id.never_close) {
mAdapter.rememberCloseAction(mCloseStateBundle,
SidebarPagerAdapter.CLOSE_ACTION_KEEP_OPEN);
cancelClosingOverlay();
}
}
public void showCloseOverlay(CloseCallback closeCallback) {
mCloseStateBundle.clear();
int closeAction = mAdapter.shouldClose(mCloseStateBundle);
// boolean showButtons;
// int delay;
switch (closeAction) {
default:
case SidebarPagerAdapter.CLOSE_ACTION_DONT_KNOW:
case SidebarPagerAdapter.CLOSE_ACTION_AUTO_CLOSE:
// showButtons = false;
// delay = DELAYED_CLOSE_DURATION;
closeCallback.close();
return;
case SidebarPagerAdapter.CLOSE_ACTION_ASK:
// showButtons = true;
// delay = DELAYED_CLOSE_DURATION_WHEN_ASKED;
break;
case SidebarPagerAdapter.CLOSE_ACTION_KEEP_OPEN:
return;
}
mClosingProgressBarAnimator.setDuration(DELAYED_CLOSE_DURATION_WHEN_ASKED);
mCancelClosingOverlay.setVisibility(View.VISIBLE);
mCancelClosingOverlay.setAlpha(0);
mCancelClosingOverlay.animate().alpha(1);
mCancelClosingProgressBar.setProgress(100);
mClosingProgressBarAnimator.start();
// if (showButtons) {
mCloseText.setVisibility(VISIBLE);
mCancelClosingOverlayAlwaysCloseButton.setVisibility(VISIBLE);
mCancelClosingOverlayNeverCloseButton.setVisibility(VISIBLE);
// } else {
// mCancelClosingOverlayAlwaysCloseButton.setVisibility(GONE);
// mCancelClosingOverlayNeverCloseButton.setVisibility(GONE);
// mCloseText.setVisibility(GONE);
// }
closeCallback.closeDelayed(DELAYED_CLOSE_DURATION_WHEN_ASKED);
}
@Override
public void onStartLoad() {
}
@Override
public void onEndLoad() {
}
public boolean getIsLeft() {
return mIsLeft;
}
public void setIsLeft(boolean left) {
mIsLeft = left;
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (!isInEditMode()) {
SharedPreferences prefs = mSharedPreferences;
prefs.registerOnSharedPreferenceChangeListener(this);
setupPagingAnimation(prefs);
mAdapter.onAttachedToWindow();
}
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mCancelClosingOverlay.setVisibility(GONE);
SharedPreferences prefs = mSharedPreferences;
prefs.unregisterOnSharedPreferenceChangeListener(this);
mAdapter.onDetachedFromWindow();
}
public void updateAdapterData(long defaultPage, CircularArray<HotspotPageEntry> entries) {
mAdapter.setPages(entries);
int idx = indexOfId(entries, defaultPage);
if (idx != -1L) {
mAppsiViewPager.setCurrentItem(idx, false);
}
}
@Override
public void onFlagsChanged(int flags) {
handleNewFlags(flags);
}
private void handleNewFlags(int flags) {
if ((flags & SidebarPagerAdapter.FLAG_NO_DECORATIONS) ==
AbstractSidebarPagerAdapter.FLAG_NO_DECORATIONS) {
mSidebarBackgroundView.animate().alpha(.5f);
mLeftShadow.animate().alpha(.5f);
mRightShadow.animate().alpha(.5f);
} else {
mSidebarBackgroundView.animate().alpha(1);
mLeftShadow.animate().alpha(1);
mRightShadow.animate().alpha(1);
}
}
public void setInFullScreenMode(boolean fullscreen) {
mAppsiContext.mIsFullScreen = fullscreen;
}
public void setSidebarOpening(boolean sidebarOpening) {
mAdapter.setSidebarOpening(sidebarOpening);
}
public void onSidebarClosing() {
mAdapter.setSidebarClosed();
}
public interface OnCancelCloseListener {
void onCloseCancelled();
}
public interface SidebarListener {
void onCloseSidebar();
}
@TargetApi(11)
public static class DepthPageTransformer extends AbstractPageTransformer {
private static final float MIN_SCALE = 0.75f;
@Override
public void transformPage(View view, float position) {
int pageWidth = view.getWidth();
reset(view);
if (position < -1) { // [-Infinity,-1)
// This page is way off-screen to the left.
} else if (position <= 0) { // [-1,0]
// Use the default slide transition when moving to the left page
view.setAlpha(1);
view.setTranslationX(0);
view.setScaleX(1);
view.setScaleY(1);
} else if (position <= 1) { // (0,1]
// Fade the page out.
view.setAlpha(1 - position);
// Counteract the default slide transition
view.setTranslationX(pageWidth * -position);
// Scale the page down (between MIN_SCALE and 1)
float scaleFactor = MIN_SCALE
+ (1 - MIN_SCALE) * (1 - Math.abs(position));
view.setScaleX(scaleFactor);
view.setScaleY(scaleFactor);
} else { // (1,+Infinity]
// This page is way off-screen to the right.
}
}
}
public static class ZoomOutPageTransformer extends AbstractPageTransformer {
private static final float MIN_SCALE = 0.85f;
private static final float MIN_ALPHA = 0.5f;
@Override
public void transformPage(View view, float position) {
int pageWidth = view.getWidth();
int pageHeight = view.getHeight();
reset(view);
if (position < -1) { // [-Infinity,-1)
// This page is way off-screen to the left.
} else if (position <= 1) { // [-1,1]
// Modify the default slide transition to shrink the page as well
float scaleFactor = Math.max(MIN_SCALE, 1 - Math.abs(position));
float vertMargin = pageHeight * (1 - scaleFactor) / 2;
float horzMargin = pageWidth * (1 - scaleFactor) / 2;
if (position < 0) {
view.setTranslationX(horzMargin - vertMargin / 2);
} else {
view.setTranslationX(-horzMargin + vertMargin / 2);
}
// Scale the page down (between MIN_SCALE and 1)
view.setScaleX(scaleFactor);
view.setScaleY(scaleFactor);
// Fade the page relative to its size.
view.setAlpha(MIN_ALPHA +
(scaleFactor - MIN_SCALE) /
(1 - MIN_SCALE) * (1 - MIN_ALPHA));
} else { // (1,+Infinity]
// This page is way off-screen to the right.
}
}
}
public static class TabletPageTransformer extends AbstractPageTransformer {
private static float MIN_SCALE = 0.85f;
private static float MIN_ALPHA = 0.5f;
public TabletPageTransformer() {
}
@Override
public void transformPage(View view, float position) {
reset(view);
if (position < -1) { // [-Infinity,-1)
// This page is way off-screen to the left.
} else if (position <= 1) { // [-1, 0]
view.setRotationY(position * -30);
} else { // (1,+Infinity]
// This page is way off-screen to the right.
}
}
}
public static class FadePageTransformer extends AbstractPageTransformer {
@Override
public void transformPage(View view, float position) {
reset(view);
if (position < -1) { // [-Infinity,-1)
// This page is way off-screen to the left.
} else if (position <= 0) { // [-1, 0]
view.setAlpha(position + 1);
} else if (position <= 1) { // [0 ,1]
view.setAlpha(1 - position);
} else { // (1,+Infinity]
// This page is way off-screen to the right.
}
}
}
static abstract class AbstractPageTransformer implements ViewPager.PageTransformer {
public void reset(View view) {
view.setAlpha(1);
view.setRotationX(0);
view.setRotationY(0);
view.setTranslationX(0);
view.setScaleX(1);
view.setScaleY(1);
}
}
class OnPageChangeListenerImpl implements ViewPager.OnPageChangeListener {
int mCurrentPage;
@Override
public void onPageScrolled(int i, float v, int i2) {
}
@Override
public void onPageSelected(int i) {
mCurrentPage = i;
}
@Override
public void onPageScrollStateChanged(int i) {
}
}
}