/* * Copyright 2016 Hippo Seven * * 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.hippo.ehviewer.ui; import android.animation.Animator; import android.annotation.SuppressLint; import android.content.DialogInterface; import android.content.Intent; import android.content.res.Resources; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.SwitchCompat; import android.view.KeyEvent; import android.view.View; import android.view.WindowManager; import android.widget.Spinner; import android.widget.TextView; import com.hippo.anani.AnimationUtils; import com.hippo.anani.SimpleAnimatorListener; import com.hippo.ehviewer.R; import com.hippo.ehviewer.Settings; import com.hippo.ehviewer.client.data.GalleryInfo; import com.hippo.ehviewer.gallery.DirGalleryProvider; import com.hippo.ehviewer.gallery.EhGalleryProvider; import com.hippo.ehviewer.gallery.GalleryProvider; import com.hippo.ehviewer.gallery.GalleryProviderListener; import com.hippo.ehviewer.gallery.ZipGalleryProvider; import com.hippo.ehviewer.gallery.gl.GalleryPageView; import com.hippo.ehviewer.gallery.gl.GalleryView; import com.hippo.ehviewer.gallery.gl.ImageView; import com.hippo.gl.glrenderer.ImageTexture; import com.hippo.gl.view.GLRootView; import com.hippo.image.Image; import com.hippo.util.SystemUiHelper; import com.hippo.widget.Slider; import com.hippo.yorozuya.ConcurrentPool; import com.hippo.yorozuya.SimpleHandler; import java.io.File; public class GalleryActivity extends AppCompatActivity implements GalleryProviderListener, Slider.OnSetProgressListener, GalleryView.Listener { public static final String ACTION_DIR = "dir"; public static final String ACTION_ZIP = "zip"; public static final String ACTION_EH = "eh"; public static final String KEY_ACTION = "action"; public static final String KEY_FILENAME = "filename"; public static final String KEY_GALLERY_INFO = "gallery_info"; public static final String KEY_SHOWING = "showing"; private static final long SLIDER_ANIMATION_DURING = 150; private static final long HIDE_SLIDER_DELAY = 3000; private String mAction; private String mFilename; private GalleryInfo mGalleryInfo; @Nullable private GalleryView mGalleryView; @Nullable private ImageTexture.Uploader mUploader; @Nullable private GalleryProvider mGalleryProvider; @Nullable private SystemUiHelper mSystemUiHelper; private boolean mSystemUiShowing = false; @Nullable private View mClock; @Nullable private View mBattery; @Nullable private View mSliderPanel; @Nullable private TextView mLeftText; @Nullable private TextView mRightText; @Nullable private Slider mSlider; private int mLayoutMode; private int mSize; private int mCurrentIndex; private final ConcurrentPool mNotifyTaskPool = new ConcurrentPool<>(3); private final Runnable mRequestLayoutSliderTask = new Runnable() { @Override public void run() { if (mSliderPanel != null) { mSliderPanel.requestLayout(); } } }; private final SimpleAnimatorListener mHideSliderListener = new SimpleAnimatorListener() { @Override public void onAnimationEnd(Animator animation) { if (mSliderPanel != null) { mSliderPanel.setVisibility(View.INVISIBLE); } } }; private final Runnable mHideSliderRunnable = new Runnable() { @Override public void run() { if (mSliderPanel != null) { hideSlider(mSliderPanel); } } }; private void buildProvider() { if (mGalleryProvider != null) { return; } if (ACTION_DIR.equals(mAction)) { if (mFilename != null) { mGalleryProvider = new DirGalleryProvider(new File(mFilename)); } } else if (ACTION_ZIP.equals(mAction)) { if (mFilename != null) { mGalleryProvider = new ZipGalleryProvider(new File(mFilename)); } } else if (ACTION_EH.equals(mAction)) { if (mGalleryInfo != null) { mGalleryProvider = new EhGalleryProvider(this, mGalleryInfo); } } } private void onInit() { Intent intent = getIntent(); if (intent == null) { return; } mAction = intent.getAction(); mFilename = intent.getStringExtra(KEY_FILENAME); mGalleryInfo = intent.getParcelableExtra(KEY_GALLERY_INFO); buildProvider(); } private void onRestore(@NonNull Bundle savedInstanceState) { mAction = savedInstanceState.getString(KEY_ACTION); mFilename = savedInstanceState.getString(KEY_FILENAME); mGalleryInfo = savedInstanceState.getParcelable(KEY_GALLERY_INFO); buildProvider(); } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putString(KEY_ACTION, mAction); outState.putString(KEY_FILENAME, mFilename); if (mGalleryInfo != null) { outState.putParcelable(KEY_GALLERY_INFO, mGalleryInfo); } outState.putBoolean(KEY_SHOWING, mSystemUiShowing); } @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (savedInstanceState == null) { onInit(); } else { onRestore(savedInstanceState); } if (mGalleryProvider == null) { finish(); return; } mGalleryProvider.setGalleryProviderListener(this); mGalleryProvider.start(); setContentView(R.layout.activity_gallery); GLRootView glRootView = (GLRootView) findViewById(R.id.gl_root_view); mGalleryProvider.setGLRoot(glRootView); mUploader = new ImageTexture.Uploader(glRootView); mGalleryView = new GalleryView(this, new GalleryAdapter(), this, Settings.getReadingDirection(), Settings.getPageScaling(), Settings.getStartPosition()); glRootView.setContentPane(mGalleryView); // System UI helper mSystemUiHelper = new SystemUiHelper(this, SystemUiHelper.LEVEL_IMMERSIVE, SystemUiHelper.FLAG_LAYOUT_IN_SCREEN_OLDER_DEVICES | SystemUiHelper.FLAG_IMMERSIVE_STICKY); if (savedInstanceState == null || !savedInstanceState.getBoolean(KEY_SHOWING, false)) { mSystemUiHelper.hide(); mSystemUiShowing = false; } else { mSystemUiHelper.show(); mSystemUiShowing = true; } mClock = findViewById(R.id.clock); mBattery = findViewById(R.id.battery); if (mClock != null && !Settings.getShowClock()) { mClock.setVisibility(View.GONE); } if (mBattery != null && !Settings.getShowBattery()) { mBattery.setVisibility(View.GONE); } mSliderPanel = findViewById(R.id.slider_panel); mLeftText = (TextView) mSliderPanel.findViewById(R.id.left); mRightText = (TextView) mSliderPanel.findViewById(R.id.right); mSlider = (Slider) mSliderPanel.findViewById(R.id.slider); mSlider.setOnSetProgressListener(this); mSize = mGalleryProvider.size(); mCurrentIndex = mGalleryView.getCurrentIndex(); mLayoutMode = mGalleryView.getLayoutMode(); updateSlider(); // Update keep screen on if (Settings.getKeepScreenOn()) { getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } else { getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } } @Override protected void onDestroy() { super.onDestroy(); mGalleryView = null; if (mUploader != null) { mUploader.clear(); mUploader = null; } if (mGalleryProvider != null) { mGalleryProvider.setGalleryProviderListener(null); mGalleryProvider.stop(); mGalleryProvider = null; } mSliderPanel = null; mLeftText = null; mRightText = null; mSlider = null; } @Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); if (hasFocus && mSystemUiHelper != null) { if (mSystemUiShowing) { mSystemUiHelper.show(); } else { mSystemUiHelper.hide(); } } } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (mGalleryView == null) { return super.onKeyDown(keyCode, event); } // Check volume if (Settings.getVolumePage()) { if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) { if (mLayoutMode == GalleryView.LAYOUT_MODE_RIGHT_TO_LEFT) { mGalleryView.pageRight(); } else { mGalleryView.pageLeft(); } return true; } else if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) { if (mLayoutMode == GalleryView.LAYOUT_MODE_RIGHT_TO_LEFT) { mGalleryView.pageLeft(); } else { mGalleryView.pageRight(); } return true; } } // Check keyboard and Dpad switch (keyCode) { case KeyEvent.KEYCODE_PAGE_UP: case KeyEvent.KEYCODE_DPAD_LEFT: case KeyEvent.KEYCODE_DPAD_UP: if (mLayoutMode == GalleryView.LAYOUT_MODE_RIGHT_TO_LEFT) { mGalleryView.pageRight(); } else { mGalleryView.pageLeft(); } return true; case KeyEvent.KEYCODE_PAGE_DOWN: case KeyEvent.KEYCODE_DPAD_RIGHT: case KeyEvent.KEYCODE_DPAD_DOWN: if (mLayoutMode == GalleryView.LAYOUT_MODE_RIGHT_TO_LEFT) { mGalleryView.pageLeft(); } else { mGalleryView.pageRight(); } return true; case KeyEvent.KEYCODE_DPAD_CENTER: case KeyEvent.KEYCODE_SPACE: case KeyEvent.KEYCODE_MENU: onTapMenuArea(); return true; } return super.onKeyDown(keyCode, event); } @Override public boolean onKeyUp(int keyCode, KeyEvent event) { // Check volume if (Settings.getVolumePage()) { if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || keyCode == KeyEvent.KEYCODE_VOLUME_UP) { return true; } } // Check keyboard and Dpad if (keyCode == KeyEvent.KEYCODE_PAGE_UP || keyCode == KeyEvent.KEYCODE_PAGE_DOWN || keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_UP || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT || keyCode == KeyEvent.KEYCODE_DPAD_DOWN || keyCode == KeyEvent.KEYCODE_DPAD_CENTER || keyCode == KeyEvent.KEYCODE_SPACE || keyCode == KeyEvent.KEYCODE_MENU) { return true; } return super.onKeyUp(keyCode, event); } private GalleryPageView findPageByIndex(int index) { if (mGalleryView != null) { return mGalleryView.findPageByIndex(index); } else { return null; } } @SuppressLint("SetTextI18n") private void updateSlider() { if (mSlider == null || mRightText == null || mLeftText == null || mSize <= 0 || mCurrentIndex < 0) { return; } TextView start; TextView end; if (mLayoutMode == GalleryView.LAYOUT_MODE_RIGHT_TO_LEFT) { start = mRightText; end = mLeftText; mSlider.setReverse(true); } else { start = mLeftText; end = mRightText; mSlider.setReverse(false); } start.setText(Integer.toString(mCurrentIndex + 1)); end.setText(Integer.toString(mSize)); mSlider.setRange(1, mSize); mSlider.setProgress(mCurrentIndex + 1); } @Override public void onDataChanged() { if (mGalleryView != null) { mGalleryView.onDataChanged(); } if (mGalleryProvider != null) { int size = mGalleryProvider.size(); NotifyTask task = mNotifyTaskPool.pop(); if (task == null) { task = new NotifyTask(); } task.setData(NotifyTask.KEY_SIZE, size); SimpleHandler.getInstance().post(task); } } @Override public void onPageWait(int index) { GalleryPageView page = findPageByIndex(index); if (page != null) { page.showInfo(); page.setImage(null); page.setPage(index + 1); page.setProgress(GalleryPageView.PROGRESS_INDETERMINATE); page.setError(null, null); } } @Override public void onPagePercent(int index, float percent) { GalleryPageView page = findPageByIndex(index); if (page != null) { page.showInfo(); page.setImage(null); page.setPage(index + 1); page.setProgress(percent); page.setError(null, null); } } @Override public void onPageSucceed(int index, Image image) { GalleryPageView page = findPageByIndex(index); if (page != null) { page.showImage(); page.setImage(image); page.setPage(index + 1); page.setProgress(GalleryPageView.PROGRESS_GONE); page.setError(null, null); } else { image.recycle(); } } @Override public void onPageFailed(int index, String error) { GalleryPageView page = findPageByIndex(index); if (page != null && mGalleryView != null) { page.showInfo(); page.setImage(null); page.setPage(index + 1); page.setProgress(GalleryPageView.PROGRESS_GONE); page.setError(error, mGalleryView); } } @Override public void onDataChanged(int index) { GalleryPageView page = findPageByIndex(index); if (page != null && mGalleryProvider != null) { mGalleryProvider.request(index); } } @Override public void onSetProgress(Slider slider, int newProgress, int oldProgress, boolean byUser, boolean confirm) { if (confirm && byUser && mGalleryView != null) { mGalleryView.setCurrentPage(newProgress - 1); } } @Override public void onFingerDown() { SimpleHandler.getInstance().removeCallbacks(mHideSliderRunnable); } @Override public void onFingerUp() { SimpleHandler.getInstance().postDelayed(mHideSliderRunnable, HIDE_SLIDER_DELAY); } @Override public void onUpdateCurrentIndex(int index) { NotifyTask task = mNotifyTaskPool.pop(); if (task == null) { task = new NotifyTask(); } task.setData(NotifyTask.KEY_CURRENT_INDEX, index); SimpleHandler.getInstance().post(task); } @Override public void onTapSliderArea() { NotifyTask task = mNotifyTaskPool.pop(); if (task == null) { task = new NotifyTask(); } task.setData(NotifyTask.KEY_TAP_SLIDER_AREA, 0); SimpleHandler.getInstance().post(task); } @Override public void onTapMenuArea() { NotifyTask task = mNotifyTaskPool.pop(); if (task == null) { task = new NotifyTask(); } task.setData(NotifyTask.KEY_TAP_MENU_AREA, 0); SimpleHandler.getInstance().post(task); } @Override public void onLongPressPage(int index) { NotifyTask task = mNotifyTaskPool.pop(); if (task == null) { task = new NotifyTask(); } task.setData(NotifyTask.KEY_LONG_PRESS_PAGE, index); SimpleHandler.getInstance().post(task); } private void showSlider(View sliderPanel) { sliderPanel.setTranslationY(sliderPanel.getHeight()); sliderPanel.setVisibility(View.VISIBLE); sliderPanel.animate().translationY(0.0f).setDuration(SLIDER_ANIMATION_DURING) .setInterpolator(AnimationUtils.FAST_SLOW_INTERPOLATOR) .setListener(null).start(); // Request layout ensure show it SimpleHandler.getInstance().post(mRequestLayoutSliderTask); } private void hideSlider(View sliderPanel) { sliderPanel.animate().translationY(sliderPanel.getHeight()).setDuration(SLIDER_ANIMATION_DURING) .setInterpolator(AnimationUtils.SLOW_FAST_INTERPOLATOR) .setListener(mHideSliderListener).start(); } private class GalleryMenuHelper implements DialogInterface.OnClickListener { private final View mView; private final Spinner mReadingDirection; private final Spinner mScaleMode; private final Spinner mStartPosition; private final SwitchCompat mKeepScreenOn; private final SwitchCompat mShowClock; private final SwitchCompat mShowBattery; private final SwitchCompat mVolumePage; @SuppressLint("InflateParams") public GalleryMenuHelper() { mView = getLayoutInflater().inflate(R.layout.dialog_gallery_menu, null); mReadingDirection = (Spinner) mView.findViewById(R.id.reading_direction); mScaleMode = (Spinner) mView.findViewById(R.id.page_scaling); mStartPosition = (Spinner) mView.findViewById(R.id.start_position); mKeepScreenOn = (SwitchCompat) mView.findViewById(R.id.keep_screen_on); mShowClock = (SwitchCompat) mView.findViewById(R.id.show_clock); mShowBattery = (SwitchCompat) mView.findViewById(R.id.show_battery); mVolumePage = (SwitchCompat) mView.findViewById(R.id.volume_page); mReadingDirection.setSelection(Settings.getReadingDirection()); mScaleMode.setSelection(Settings.getPageScaling()); mStartPosition.setSelection(Settings.getStartPosition()); mKeepScreenOn.setChecked(Settings.getKeepScreenOn()); mShowClock.setChecked(Settings.getShowClock()); mShowBattery.setChecked(Settings.getShowBattery()); mVolumePage.setChecked(Settings.getVolumePage()); } public View getView() { return mView; } @Override public void onClick(DialogInterface dialog, int which) { int layoutMode = GalleryView.sanitizeLayoutMode(mReadingDirection.getSelectedItemPosition()); int scaleMode = ImageView.sanitizeScaleMode(mScaleMode.getSelectedItemPosition()); int startPosition = ImageView.sanitizeStartPosition(mStartPosition.getSelectedItemPosition()); boolean keepScreenOn = mKeepScreenOn.isChecked(); boolean showClock = mShowClock.isChecked(); boolean showBattery = mShowBattery.isChecked(); boolean volumePage = mVolumePage.isChecked(); Settings.putReadingDirection(layoutMode); Settings.putPageScaling(scaleMode); Settings.putStartPosition(startPosition); Settings.putKeepScreenOn(keepScreenOn); Settings.putShowClock(showClock); Settings.putShowBattery(showBattery); Settings.putVolumePage(volumePage); if (mGalleryView != null) { mGalleryView.setLayoutMode(layoutMode); mGalleryView.setScaleMode(scaleMode); mGalleryView.setStartPosition(startPosition); } if (keepScreenOn) { getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } else { getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } if (mClock != null) { mClock.setVisibility(showClock ? View.VISIBLE : View.GONE); } if (mBattery != null) { mBattery.setVisibility(showBattery ? View.VISIBLE : View.GONE); } // Update slider mLayoutMode = layoutMode; updateSlider(); } } private class NotifyTask implements Runnable { public static final int KEY_LAYOUT_MODE = 0; public static final int KEY_SIZE = 1; public static final int KEY_CURRENT_INDEX = 2; public static final int KEY_TAP_SLIDER_AREA = 3; public static final int KEY_TAP_MENU_AREA = 4; public static final int KEY_LONG_PRESS_PAGE = 5; private int mKey; private int mValue; public void setData(int key, int value) { mKey = key; mValue = value; } private void onTapMenuArea() { GalleryMenuHelper helper = new GalleryMenuHelper(); new AlertDialog.Builder(GalleryActivity.this) .setTitle(R.string.gallery_menu_title) .setView(helper.getView()) .setPositiveButton(android.R.string.ok, helper).show(); } private void onTapSliderArea() { if (mSliderPanel == null || mSize <= 0 || mCurrentIndex < 0) { return; } SimpleHandler.getInstance().removeCallbacks(mHideSliderRunnable); if (mSliderPanel.getVisibility() == View.VISIBLE) { hideSlider(mSliderPanel); } else { showSlider(mSliderPanel); SimpleHandler.getInstance().postDelayed(mHideSliderRunnable, HIDE_SLIDER_DELAY); } } private void onLongPressPage(final int index) { Resources resources = GalleryActivity.this.getResources(); new AlertDialog.Builder(GalleryActivity.this) .setTitle(resources.getString(R.string.page_menu_title, index + 1)) .setItems(R.array.page_menu_entries, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { if (mGalleryProvider == null) { return; } switch (which) { case 0: // Refresh mGalleryProvider.forceRequest(index); break; case 1: // Share // TODO break; case 2: // Save // TODO break; case 3: // Add a bookmark break; } } }).show(); } @Override public void run() { switch (mKey) { case KEY_LAYOUT_MODE: GalleryActivity.this.mLayoutMode = mValue; updateSlider(); break; case KEY_SIZE: GalleryActivity.this.mSize = mValue; updateSlider(); break; case KEY_CURRENT_INDEX: GalleryActivity.this.mCurrentIndex = mValue; updateSlider(); break; case KEY_TAP_MENU_AREA: onTapMenuArea(); break; case KEY_TAP_SLIDER_AREA: onTapSliderArea(); break; case KEY_LONG_PRESS_PAGE: onLongPressPage(mValue); break; } mNotifyTaskPool.push(this); } } private class GalleryAdapter extends GalleryView.Adapter { @Override public String getError() { if (mGalleryProvider == null) { return getString(R.string.error_no_provider); } else if (mGalleryProvider.size() <= GalleryProvider.STATE_ERROR) { return mGalleryProvider.getError(); } return null; } @Override public int size() { if (mGalleryProvider == null) { return GalleryProvider.STATE_ERROR; } else { return mGalleryProvider.size(); } } @Override public void onBind(GalleryPageView view, int index) { if (mGalleryProvider != null && mGalleryView != null) { mGalleryProvider.request(index); view.showInfo(); view.setImage(null); view.setPage(index + 1); view.setProgress(GalleryPageView.PROGRESS_INDETERMINATE); view.setError(null, null); } } @Override public void onUnbind(GalleryPageView view) { view.setImage(null); view.setError(null, null); } } }