package com.bitmovin.bitcodin.Overlay; import android.app.Activity; import android.content.Context; import android.content.SharedPreferences; import android.text.Editable; import android.text.TextWatcher; import android.view.KeyEvent; import android.view.View; import android.view.animation.AlphaAnimation; import android.view.animation.Animation; import android.view.animation.Transformation; import android.view.inputmethod.InputMethodManager; import android.widget.EditText; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.RelativeLayout; import android.widget.TextView; import com.bitmovin.bitcodin.Job.BitcodinJob; import com.bitmovin.bitcodin.Job.JobManager; import com.bitmovin.bitcodin.Job.JobManagerListener; import com.bitmovin.bitcodin.R; import com.bitmovin.bitcodin.Settings; import com.bitmovin.bitcodin.Utils; import java.util.ArrayList; import java.util.Timer; import java.util.TimerTask; @SuppressWarnings({"FieldCanBeLocal", "unused"}) public class SidebarOverlay implements JobManagerListener { private Context context; private JobManager mJobManager; private LinearLayout sidebarWrapper; /* sidebar header */ private LinearLayout sidebarHeader; private ImageButton settingsButton; private EditText currentPage; private TextView totalPages; private ImageButton nextPage; private ImageButton previousPage; private ImageButton logout; /* loading view */ private LinearLayout loadWrapper; private ProgressBar loadingProgressBar; /* joblist view */ private LinearLayout joblistWrapper; private LinearLayout joblistContainer; private TextView noJobs; /* settings view */ private LinearLayout settingsWrapper; private EditText defaultWideVineUri; /* sidebar handle */ private RelativeLayout showSidebarWrapper; private ImageView showSidebarButton; /* animations */ private SlideAnimation slideInSidebar; private SlideAnimation slideOutSidebar; private AlphaAnimation fadeInShowSidebarWrapper; private AlphaAnimation fadeOutShowSidebarWrapper; /* general */ private boolean sidebarVisible; private boolean showSidebarWrapperVisible; private Timer fadeOutShowSidebarWrapperTimer; private TimerTask fadeOutShowSidebarWrapperTimerTask; private long numPages; private int lastPage; private ArrayList<SidebarOverlayListener> mListeners; private boolean loadingVisible; private boolean joblistVisible; private boolean settingsVisible; private SharedPreferences mPreferences; private SharedPreferences.Editor mPreferencesEditor; private enum FadingViews { SHOW_SIDEBAR_WRAPPER } public SidebarOverlay(final Context context, LinearLayout sidebarWrapper, JobManager mJobManager) { this.context = context; this.sidebarWrapper = sidebarWrapper; this.mJobManager = mJobManager; if (this.sidebarWrapper != null) { /* sidebar header */ this.sidebarHeader = (LinearLayout) this.sidebarWrapper.findViewById(R.id.player_llt_sidebar_header_container); this.settingsButton = (ImageButton) this.sidebarWrapper.findViewById(R.id.player_ib_settings); this.currentPage = (EditText) this.sidebarWrapper.findViewById(R.id.player_et_pager_current); this.totalPages = (TextView) this.sidebarWrapper.findViewById(R.id.player_tv_pager_total); this.nextPage = (ImageButton) this.sidebarWrapper.findViewById(R.id.player_ib_pager_next); this.previousPage = (ImageButton) this.sidebarWrapper.findViewById(R.id.player_ib_pager_prev); this.logout = (ImageButton) this.sidebarWrapper.findViewById(R.id.player_ib_logout); /* loading view */ this.loadWrapper = (LinearLayout) this.sidebarWrapper.findViewById(R.id.player_llt_sidebar_loading); this.loadingProgressBar = (ProgressBar) this.sidebarWrapper.findViewById(R.id.player_pb_sidebar_loading_progress); /* joblist view */ this.joblistWrapper = (LinearLayout) this.sidebarWrapper.findViewById(R.id.player_llt_sidebar_joblist); this.joblistContainer = (LinearLayout) this.sidebarWrapper.findViewById(R.id.player_llt_joblist_container); this.noJobs = (TextView) this.sidebarWrapper.findViewById(R.id.player_tv_no_jobs); /* settings view */ this.settingsWrapper = (LinearLayout) this.sidebarWrapper.findViewById(R.id.player_llt_sidebar_settings); this.defaultWideVineUri = (EditText) this.settingsWrapper.findViewById(R.id.player_et_widevine_uri); /* sidebar handle */ this.showSidebarWrapper = (RelativeLayout) this.sidebarWrapper.findViewById(R.id.player_rlt_show_sidebar); this.showSidebarButton = (ImageView) this.sidebarWrapper.findViewById(R.id.player_iv_show_sidebar); this.showSidebarWrapper.setOnClickListener(this.onShowSidebarWrapperClickListener); this.sidebarVisible = true; this.showSidebarWrapperVisible = true; this.numPages = -1; this.lastPage = -1; this.mListeners = new ArrayList<>(); this.loadingVisible = false; this.joblistVisible = false; this.settingsVisible = false; this.settingsButton.setOnClickListener(this.onSettingsButtonClick); this.logout.setOnClickListener(this.onLogoutClickListener); this.defaultWideVineUri.setText(Settings.WIDEVINE_GTS_DEFAULT_BASE_URI); this.defaultWideVineUri.addTextChangedListener(this.onWideVineUriChanged); this.mPreferences = this.context.getSharedPreferences("bitcodin", Context.MODE_PRIVATE); this.mPreferencesEditor = this.mPreferences.edit(); setupAnimations(); this.mJobManager.addListener(this); this.mJobManager.loadJobs(0); } } public void addListener(SidebarOverlayListener listener) { if (listener != null) { this.mListeners.add(listener); } } // region animations and visibility private void setupAnimations() { this.slideInSidebar = new SlideAnimation( this.sidebarWrapper, Settings.SLIDE_DURATION, SlideAnimation.Direction.LEFT, -this.context.getResources().getDimension(R.dimen.player_rlt_sidebar_width), 0); this.slideInSidebar.setAnimationListener(this.sidebarAnimationListener); this.slideOutSidebar = new SlideAnimation( this.sidebarWrapper, Settings.SLIDE_DURATION, SlideAnimation.Direction.LEFT, 0, -this.context.getResources().getDimension(R.dimen.player_rlt_sidebar_width)); this.slideOutSidebar.setAnimationListener(this.sidebarAnimationListener); this.fadeInShowSidebarWrapper = new AlphaAnimation(0, 1); this.fadeInShowSidebarWrapper.setDuration(Settings.FADE_IN_DURATION); this.fadeInShowSidebarWrapper.setAnimationListener(this.sidebarAnimationListener); this.fadeOutShowSidebarWrapper = new AlphaAnimation(1, 0); this.fadeOutShowSidebarWrapper.setDuration(Settings.FADE_OUT_DURATION); this.fadeOutShowSidebarWrapper.setAnimationListener(this.sidebarAnimationListener); } private Animation.AnimationListener sidebarAnimationListener = new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { if (animation == fadeInShowSidebarWrapper) { showSidebarWrapper.setVisibility(View.VISIBLE); showSidebarWrapperVisible = true; } else if (animation == fadeOutShowSidebarWrapper) { showSidebarWrapperVisible = false; } } @Override public void onAnimationEnd(Animation animation) { if (animation == slideInSidebar) { showSidebarButton.setBackgroundResource(R.drawable.collapse_sidebar); sidebarVisible = true; } else if (animation == slideOutSidebar) { showSidebarButton.setBackgroundResource(R.drawable.expand_sidebar); sidebarVisible = false; fadeView(false, FadingViews.SHOW_SIDEBAR_WRAPPER, false); } else if (animation == fadeOutShowSidebarWrapper) { showSidebarWrapper.setVisibility(View.GONE); } else if (animation == fadeInShowSidebarWrapper) { setupFadeoutTimer(); } } @Override public void onAnimationRepeat(Animation animation) { } }; private void setupFadeoutTimer() { this.fadeOutShowSidebarWrapperTimerTask = new TimerTask() { @Override public void run() { showSidebarWrapper.post(new Runnable() { @Override public void run() { fadeView(false, FadingViews.SHOW_SIDEBAR_WRAPPER, false); } }); } }; this.fadeOutShowSidebarWrapperTimer = new Timer(); this.fadeOutShowSidebarWrapperTimer.schedule( fadeOutShowSidebarWrapperTimerTask, Settings.FADE_OUT_TIMEOUT ); } public void show() { if (!this.sidebarVisible) { fadeView(true, FadingViews.SHOW_SIDEBAR_WRAPPER, false); } } public void hide() { if (this.sidebarVisible) { this.sidebarWrapper.startAnimation(this.slideOutSidebar); this.sidebarVisible = false; } fadeView(false, FadingViews.SHOW_SIDEBAR_WRAPPER, true); } private void fadeView(boolean fadeIn, FadingViews view, boolean force) { Animation mAnimation; switch (view) { case SHOW_SIDEBAR_WRAPPER: if (!(this.showSidebarWrapperVisible ^ fadeIn) && !force) break; if (!fadeIn && this.sidebarVisible && !force) break; mAnimation = fadeIn ? this.fadeInShowSidebarWrapper : this.fadeOutShowSidebarWrapper; this.showSidebarWrapper.clearAnimation(); mAnimation.reset(); this.showSidebarWrapper.setAnimation(mAnimation); mAnimation.start(); if (!fadeIn) { try { this.fadeOutShowSidebarWrapperTimer.cancel(); } catch (Exception ignore) { } } break; } } private void setNoJobsVisible(final boolean visible) { this.noJobs.post(new Runnable() { @Override public void run() { noJobs.setVisibility(visible ? View.VISIBLE : View.GONE); } }); } private void setSidebarHeaderEnabled(final boolean enabled) { this.sidebarHeader.post(new Runnable() { @Override public void run() { int numChildren = sidebarHeader.getChildCount(); for (int idx = 0; idx < numChildren; idx++) { sidebarHeader.getChildAt(idx).setEnabled(enabled); } } }); } private void setLoadingViewVisible(final boolean visible, boolean updateState) { if (updateState) this.loadingVisible = visible; if (this.settingsVisible && updateState) return; this.loadWrapper.post(new Runnable() { @Override public void run() { loadWrapper.setVisibility(visible ? View.VISIBLE : View.GONE); } }); } private void setJoblistViewVisible(final boolean visible, boolean updateState) { if (updateState) this.joblistVisible = visible; if (this.settingsVisible && updateState) return; this.joblistWrapper.post(new Runnable() { @Override public void run() { joblistWrapper.setVisibility(visible ? View.VISIBLE : View.GONE); } }); } private void setSettingsVisible(final boolean visible) { this.settingsVisible = visible; if (visible) { setLoadingViewVisible(false, false); setJoblistViewVisible(false, false); } this.settingsWrapper.post(new Runnable() { @Override public void run() { settingsWrapper.setVisibility(visible ? View.VISIBLE : View.GONE); } }); if (!visible) { setLoadingViewVisible(this.loadingVisible, false); setJoblistViewVisible(this.joblistVisible, false); } } // endregion animations and visibility // region jobManager callbacks @Override public void onStartLoading() { setLoadingViewVisible(true, true); setJoblistViewVisible(false, true); setSidebarHeaderEnabled(false); } @Override public void onNumJobsAvailable(final long numJobs, long perPage) { this.numPages = (int) Math.ceil((float) numJobs / (float) perPage); this.previousPage.setOnClickListener(this.onPagePrevNextClickListener); this.nextPage.setOnClickListener(this.onPagePrevNextClickListener); this.currentPage.setOnKeyListener(this.currentPageKeyListener); this.sidebarWrapper.post(new Runnable() { @Override public void run() { totalPages.setText("/ " + ((numPages > 0) ? numPages : "1")); if (lastPage > 0) { currentPage.setText("" + lastPage); } else { currentPage.setText("1"); lastPage = 1; } } }); } @Override public void onProgressChanged(final double progress) { //this.loadingProgressBar.post(new Runnable() { // @Override // public void run() { // loadingProgressBar.setProgress((int) (progress * 1000.0)); // } //}); } @Override public void onJobsLoaded(int numJobs) { setLoadingViewVisible(false, true); setJoblistViewVisible(true, true); setSidebarHeaderEnabled(true); if (numJobs <= 0) { setNoJobsVisible(true); } else { setNoJobsVisible(false); } } @Override public void onSourceSelected(BitcodinJob.Source src, BitcodinJob job) { if (this.mListeners != null) { for (SidebarOverlayListener listener : this.mListeners) { listener.onSourceSelected(src, job); } } } // endregion jobManager callbacks // region event listeners private View.OnClickListener onLogoutClickListener = new View.OnClickListener() { @Override public void onClick(View v) { SharedPreferences.Editor mSharedPreferenceEditor = context.getSharedPreferences("bitcodin", Context.MODE_PRIVATE).edit(); mSharedPreferenceEditor.remove("current_api_key"); mSharedPreferenceEditor.commit(); ((Activity)context).finishAffinity(); } }; private View.OnClickListener onSettingsButtonClick = new View.OnClickListener() { @Override public void onClick(View v) { setSettingsVisible(settingsVisible = !settingsVisible); } }; private TextWatcher onWideVineUriChanged = new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { } @Override public void afterTextChanged(Editable s) { Settings.WIDEVINE_GTS_DEFAULT_BASE_URI = defaultWideVineUri.getText().toString(); mPreferencesEditor.putString( Settings.USER_FOLDER + "widevine_url", Settings.WIDEVINE_GTS_DEFAULT_BASE_URI ); mPreferencesEditor.commit(); } }; private View.OnKeyListener currentPageKeyListener = new View.OnKeyListener() { @Override public boolean onKey(View v, int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_ENTER) { int currentVal = parseIntSafe(currentPage.getText().toString(), -1); if (currentVal < 1) { currentPage.setText("1"); if (lastPage != 1) { lastPage = 1; mJobManager.loadJobs(lastPage); } } else if (currentVal > numPages) { currentPage.setText("" + numPages); if (lastPage != (int) numPages) { lastPage = (int) numPages; mJobManager.loadJobs(lastPage); } } else { lastPage = currentVal; mJobManager.loadJobs(lastPage); } InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(v.getWindowToken(), 0); Utils.hideSystemUI(context, true); return true; } else if (keyCode == KeyEvent.KEYCODE_BACK) { Utils.hideSystemUI(context, true); return false; } return false; } }; private View.OnClickListener onPagePrevNextClickListener = new View.OnClickListener() { @Override public void onClick(View v) { int currentValue = parseIntSafe(currentPage.getText().toString(), -1); if (currentValue > numPages) currentValue = (int) numPages; if (currentValue < 1) currentValue = 1; currentPage.setText("" + currentValue); if (v == previousPage) { if (currentValue > 1) { lastPage = currentValue - 1; currentPage.setText("" + lastPage); } } else if (v == nextPage) { if (currentValue < numPages) { lastPage = currentValue + 1; currentPage.setText("" + lastPage); } } mJobManager.loadJobs(lastPage); } }; private View.OnClickListener onShowSidebarWrapperClickListener = new View.OnClickListener() { @Override public void onClick(View v) { if (!sidebarVisible) { fadeView(true, FadingViews.SHOW_SIDEBAR_WRAPPER, false); try { fadeOutShowSidebarWrapperTimer.cancel(); } catch (Exception ignore) { } sidebarWrapper.startAnimation(slideInSidebar); sidebarVisible = true; } else { sidebarWrapper.startAnimation(slideOutSidebar); sidebarVisible = false; } } }; // endregion event listeners private int parseIntSafe(String str, int defValue) { try { return Integer.parseInt(str); } catch (Exception ignore) { return defValue; } } private class SlideAnimation extends Animation { private View mView; private RelativeLayout.LayoutParams mLayoutParams; private String direction; private float pxStart; private float pxEnd; public SlideAnimation(View mView, long duration, String direction, float pxStart, float pxEnd) { this.mView = mView; this.mLayoutParams = (RelativeLayout.LayoutParams) this.mView.getLayoutParams(); this.direction = direction; this.pxStart = pxStart; this.pxEnd = pxEnd; this.setDuration(duration); } @Override protected void applyTransformation(float interpolatedTime, Transformation t) { int currentPx = (int) (pxStart + ((pxEnd - pxStart) * interpolatedTime)); switch (this.direction) { case Direction.LEFT: this.mLayoutParams.leftMargin = currentPx; break; case Direction.RIGHT: this.mLayoutParams.rightMargin = currentPx; break; case Direction.TOP: this.mLayoutParams.topMargin = currentPx; break; case Direction.BOTTOM: this.mLayoutParams.bottomMargin = currentPx; break; } this.mView.setLayoutParams(this.mLayoutParams); } public final class Direction { public static final String LEFT = "left"; public static final String RIGHT = "right"; public static final String TOP = "top"; public static final String BOTTOM = "bottom"; } } }