Initial support for custom images scaling (#40)

This commit is contained in:
inorichi 2016-01-27 01:48:40 +01:00
parent c6ecfb2f67
commit 0c9bc97fe8
19 changed files with 127 additions and 91 deletions

View file

@ -328,7 +328,6 @@ public class DownloadManager {
// Return the page list from the chapter's directory if it exists, null otherwise
public List<Page> getSavedPageList(Source source, Manga manga, Chapter chapter) {
List<Page> pages = null;
File chapterDir = getAbsoluteChapterDirectory(source, manga, chapter);
File pagesFile = new File(chapterDir, PAGE_LIST_FILE);
@ -337,14 +336,14 @@ public class DownloadManager {
if (pagesFile.exists()) {
reader = new JsonReader(new FileReader(pagesFile.getAbsolutePath()));
Type collectionType = new TypeToken<List<Page>>() {}.getType();
pages = gson.fromJson(reader, collectionType);
return gson.fromJson(reader, collectionType);
}
} catch (Exception e) {
Timber.e(e.getCause(), e.getMessage());
} finally {
if (reader != null) try { reader.close(); } catch (IOException e) { /* Do nothing */ }
}
return pages;
return null;
}
// Shortcut for the method above

View file

@ -47,7 +47,7 @@ public class DownloadQueue extends ArrayList<Download> {
}
public Observable<Download> getProgressObservable() {
return statusSubject
return statusSubject.onBackpressureBuffer()
.startWith(getActiveDownloads())
.flatMap(download -> {
if (download.getStatus() == Download.DOWNLOADING) {

View file

@ -88,6 +88,10 @@ public class PreferencesHelper {
return prefs.getInt(getKey(R.string.pref_default_viewer_key), 1);
}
public Preference<Integer> imageScaleType() {
return rxPrefs.getInteger(getKey(R.string.pref_image_scale_type_key), 1);
}
public Preference<Integer> portraitColumns() {
return rxPrefs.getInteger(getKey(R.string.pref_library_columns_portrait_key), 0);
}

View file

@ -7,6 +7,7 @@ import android.view.Gravity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.view.animation.Animation;
@ -44,7 +45,7 @@ public class ReaderMenu {
@Bind(R.id.lock_orientation) ImageButton lockOrientation;
@Bind(R.id.reader_selector) ImageButton readerSelector;
@Bind(R.id.reader_extra_settings) ImageButton extraSettings;
@Bind(R.id.reader_brightness) ImageButton brightnessSettings;
@Bind(R.id.reader_scale_type_selector) ImageButton scaleTypeSelector;
private MenuItem nextChapterBtn;
private MenuItem prevChapterBtn;
@ -56,7 +57,6 @@ public class ReaderMenu {
@State boolean showing;
private PopupWindow settingsPopup;
private PopupWindow brightnessPopup;
private boolean inverted;
private DecimalFormat decimalFormat;
@ -73,7 +73,7 @@ public class ReaderMenu {
decimalFormat = new DecimalFormat("#.###");
inverted = false;
initializeOptions();
initializeMenu();
}
public void add(Subscription subscription) {
@ -110,7 +110,6 @@ public class ReaderMenu {
bottomMenu.startAnimation(bottomMenuAnimation);
settingsPopup.dismiss();
brightnessPopup.dismiss();
showing = false;
}
@ -175,7 +174,7 @@ public class ReaderMenu {
if (nextChapterBtn != null) nextChapterBtn.setVisible(nextChapter != null);
}
private void initializeOptions() {
private void initializeMenu() {
// Orientation changes
add(preferences.lockOrientation().asObservable()
.subscribe(locked -> {
@ -190,6 +189,18 @@ public class ReaderMenu {
lockOrientation.setOnClickListener(v ->
preferences.lockOrientation().set(!preferences.lockOrientation().get()));
// Scale type selector
scaleTypeSelector.setOnClickListener(v -> {
showImmersiveDialog(new MaterialDialog.Builder(activity)
.items(R.array.image_scale_type)
.itemsCallbackSingleChoice(preferences.imageScaleType().get() - 1,
(d, itemView, which, text) -> {
preferences.imageScaleType().set(which + 1);
return true;
})
.build());
});
// Reader selector
readerSelector.setOnClickListener(v -> {
final Manga manga = activity.getPresenter().getManga();
@ -215,17 +226,6 @@ public class ReaderMenu {
settingsPopup.dismiss();
});
// Brightness popup
final View brightnessView = activity.getLayoutInflater().inflate(R.layout.reader_brightness, null);
brightnessPopup = new BrightnessPopupWindow(brightnessView);
brightnessSettings.setOnClickListener(v -> {
if (!brightnessPopup.isShowing())
brightnessPopup.showAtLocation(brightnessSettings,
Gravity.BOTTOM | Gravity.LEFT, 0, bottomMenu.getHeight());
else
brightnessPopup.dismiss();
});
}
private void showImmersiveDialog(Dialog dialog) {
@ -247,8 +247,11 @@ public class ReaderMenu {
@Bind(R.id.hide_status_bar) CheckBox hideStatusBar;
@Bind(R.id.keep_screen_on) CheckBox keepScreenOn;
@Bind(R.id.reader_theme) CheckBox readerTheme;
@Bind(R.id.image_decoder_container) ViewGroup imageDecoderContainer;
@Bind(R.id.image_decoder) TextView imageDecoder;
@Bind(R.id.image_decoder_initial) TextView imageDecoderInitial;
@Bind(R.id.custom_brightness) CheckBox customBrightness;
@Bind(R.id.brightness_seekbar) SeekBar brightnessSeekbar;
public SettingsPopupWindow(View view) {
super(view, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
@ -282,7 +285,7 @@ public class ReaderMenu {
readerTheme.setOnCheckedChangeListener((view, isChecked) ->
preferences.readerTheme().set(isChecked ? 1 : 0));
imageDecoder.setOnClickListener(v -> {
imageDecoderContainer.setOnClickListener(v -> {
showImmersiveDialog(new MaterialDialog.Builder(activity)
.title(R.string.pref_image_decoder)
.items(R.array.image_decoders)
@ -294,6 +297,21 @@ public class ReaderMenu {
})
.build());
});
add(preferences.customBrightness()
.asObservable()
.subscribe(isEnabled -> {
customBrightness.setChecked(isEnabled);
brightnessSeekbar.setEnabled(isEnabled);
}));
customBrightness.setOnCheckedChangeListener((view, isChecked) ->
preferences.customBrightness().set(isChecked));
brightnessSeekbar.setMax(100);
brightnessSeekbar.setProgress(Math.round(
preferences.customBrightnessValue().get() * brightnessSeekbar.getMax()));
brightnessSeekbar.setOnSeekBarChangeListener(new BrightnessSeekBarChangeListener());
}
private void setDecoderInitial(int decoder) {
@ -314,37 +332,6 @@ public class ReaderMenu {
}
class BrightnessPopupWindow extends PopupWindow {
@Bind(R.id.custom_brightness) CheckBox customBrightness;
@Bind(R.id.brightness_seekbar) SeekBar brightnessSeekbar;
public BrightnessPopupWindow(View view) {
super(view, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
setAnimationStyle(R.style.reader_brightness_popup_animation);
ButterKnife.bind(this, view);
initializePopupMenu();
}
private void initializePopupMenu() {
add(preferences.customBrightness()
.asObservable()
.subscribe(isEnabled -> {
customBrightness.setChecked(isEnabled);
brightnessSeekbar.setEnabled(isEnabled);
}));
customBrightness.setOnCheckedChangeListener((view, isChecked) ->
preferences.customBrightness().set(isChecked));
brightnessSeekbar.setMax(100);
brightnessSeekbar.setProgress(Math.round(
preferences.customBrightnessValue().get() * brightnessSeekbar.getMax()));
brightnessSeekbar.setOnSeekBarChangeListener(new BrightnessSeekBarChangeListener());
}
}
class PageSeekBarChangeListener implements SeekBar.OnSeekBarChangeListener {
@Override

View file

@ -22,6 +22,8 @@ public abstract class PagerReader extends BaseReader {
protected boolean transitions;
protected CompositeSubscription subscriptions;
protected int scaleType = 1;
protected void initializePager(Pager pager) {
this.pager = pager;
pager.setLayoutParams(new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
@ -66,6 +68,13 @@ public abstract class PagerReader extends BaseReader {
.distinctUntilChanged()
.subscribe(v -> adapter.notifyDataSetChanged()));
subscriptions.add(getReaderActivity().getPreferences().imageScaleType()
.asObservable()
.doOnNext(this::setImageScaleType)
.skip(1)
.distinctUntilChanged()
.subscribe(v -> adapter.notifyDataSetChanged()));
subscriptions.add(getReaderActivity().getPreferences().enableTransitions()
.asObservable()
.subscribe(value -> transitions = value));
@ -110,6 +119,10 @@ public abstract class PagerReader extends BaseReader {
return pager.onImageTouch(motionEvent);
}
private void setImageScaleType(int scaleType) {
this.scaleType = scaleType;
}
public abstract void onFirstPageOut();
public abstract void onLastPageOut();

View file

@ -25,7 +25,6 @@ import eu.kanade.tachiyomi.R;
import eu.kanade.tachiyomi.data.source.model.Page;
import eu.kanade.tachiyomi.ui.base.fragment.BaseFragment;
import eu.kanade.tachiyomi.ui.reader.ReaderActivity;
import eu.kanade.tachiyomi.ui.reader.viewer.base.BaseReader;
import rx.Observable;
import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers;
@ -55,7 +54,7 @@ public class PagerReaderFragment extends BaseFragment {
View view = inflater.inflate(R.layout.item_pager_reader, container, false);
ButterKnife.bind(this, view);
ReaderActivity activity = getReaderActivity();
BaseReader parentFragment = (BaseReader) getParentFragment();
PagerReader parentFragment = (PagerReader) getParentFragment();
if (activity.getReaderTheme() == ReaderActivity.BLACK_THEME) {
progressText.setTextColor(ContextCompat.getColor(getContext(), R.color.light_grey));
@ -65,7 +64,7 @@ public class PagerReaderFragment extends BaseFragment {
imageView.setMaxDimensions(activity.getMaxBitmapSize(), activity.getMaxBitmapSize());
imageView.setDoubleTapZoomStyle(SubsamplingScaleImageView.ZOOM_FOCUS_FIXED);
imageView.setPanLimit(SubsamplingScaleImageView.PAN_LIMIT_INSIDE);
imageView.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CENTER_INSIDE);
imageView.setMinimumScaleType(parentFragment.scaleType);
imageView.setRegionDecoderClass(parentFragment.getRegionDecoderClass());
imageView.setOnTouchListener((v, motionEvent) -> parentFragment.onImageTouch(motionEvent));
imageView.setOnImageEventListener(new SubsamplingScaleImageView.DefaultOnImageEventListener() {

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 235 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 332 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 510 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 584 B

View file

@ -1,23 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="@color/reader_menu_background"
android:paddingRight="10dp"
android:paddingLeft="5dp"
android:paddingTop="5dp"
android:paddingBottom="5dp">
<CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/reader_menu_settings_item"
android:text="@string/pref_custom_brightness"
android:id="@+id/custom_brightness" />
<SeekBar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/brightness_seekbar" />
</LinearLayout>

View file

@ -74,18 +74,19 @@
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:id="@+id/reader_brightness"
android:src="@drawable/ic_brightness_high"
android:id="@+id/lock_orientation"
android:src="@drawable/ic_screen_rotation"
android:layout_gravity="center_vertical"
android:background="?android:selectableItemBackground" />
<ImageButton
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:id="@+id/lock_orientation"
android:src="@drawable/ic_screen_rotation"
android:id="@+id/reader_scale_type_selector"
android:src="@drawable/ic_zoom_out_map_white_24dp"
android:layout_gravity="center_vertical"
android:background="?android:selectableItemBackground" />
<ImageButton
android:layout_width="0dp"
android:layout_height="match_parent"

View file

@ -10,7 +10,8 @@
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
android:layout_height="wrap_content"
android:id="@+id/image_decoder_container">
<TextView
android:id="@+id/image_decoder_initial"
@ -53,4 +54,16 @@
style="@style/reader_menu_settings_item"
android:text="@string/pref_keep_screen_on"/>
<CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/reader_menu_settings_item"
android:text="@string/pref_custom_brightness"
android:id="@+id/custom_brightness" />
<SeekBar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/brightness_seekbar" />
</LinearLayout>

View file

@ -48,6 +48,22 @@
<item>1</item>
</string-array>
<string-array name="image_scale_type">
<item>@string/scale_type_fit_screen</item>
<item>@string/scale_type_stretch</item>
<item>@string/scale_type_fit_width</item>
<item>@string/scale_type_fit_height</item>
<item>@string/scale_type_original_size</item>
</string-array>
<string-array name="image_scale_type_values">
<item>1</item>
<item>2</item>
<item>3</item>
<item>4</item>
<item>5</item>
</string-array>
<string-array name="library_update_interval">
<item>@string/update_never</item>
<item>@string/update_1hour</item>

View file

@ -16,6 +16,7 @@
<string name="pref_ask_update_manga_sync_key">pref_ask_update_manga_sync_key</string>
<string name="pref_default_viewer_key">pref_default_viewer_key</string>
<string name="pref_image_scale_type_key">pref_image_scale_type_key</string>
<string name="pref_hide_status_bar_key">pref_hide_status_bar_key</string>
<string name="pref_lock_orientation_key">pref_lock_orientation_key</string>
<string name="pref_enable_transitions_key">pref_enable_transitions_key</string>

View file

@ -97,6 +97,12 @@
<string name="pref_image_decoder">Image decoder</string>
<string name="rapid_decoder">Rapid</string>
<string name="skia_decoder">Skia</string>
<string name="pref_image_scale_type">Scale type</string>
<string name="scale_type_fit_screen">Fit screen</string>
<string name="scale_type_stretch">Stretch</string>
<string name="scale_type_fit_width">Fit width</string>
<string name="scale_type_fit_height">Fit height</string>
<string name="scale_type_original_size">Original size</string>
<!-- Downloads section -->
<string name="pref_download_directory">Downloads directory</string>

View file

@ -25,6 +25,14 @@
android:defaultValue="1"
android:summary="%s"/>
<eu.kanade.tachiyomi.widget.preference.IntListPreference
android:title="@string/pref_image_scale_type"
android:key="@string/pref_image_scale_type_key"
android:entries="@array/image_scale_type"
android:entryValues="@array/image_scale_type_values"
android:defaultValue="1"
android:summary="%s"/>
<eu.kanade.tachiyomi.widget.preference.IntListPreference
android:title="@string/pref_reader_theme"
android:key="@string/pref_reader_theme_key"

View file

@ -121,10 +121,14 @@ public class SubsamplingScaleImageView extends View {
public static final int SCALE_TYPE_CENTER_INSIDE = 1;
/** Scale the image uniformly so that both dimensions of the image will be equal to or larger than the corresponding dimension of the view. The image is then centered in the view. */
public static final int SCALE_TYPE_CENTER_CROP = 2;
public static final int SCALE_TYPE_FIT_WIDTH = 3;
public static final int SCALE_TYPE_FIT_HEIGHT = 4;
public static final int SCALE_TYPE_ORIGINAL_SIZE = 5;
/** Scale the image so that both dimensions of the image will be equal to or less than the maxScale and equal to or larger than minScale. The image is then centered in the view. */
public static final int SCALE_TYPE_CUSTOM = 3;
public static final int SCALE_TYPE_CUSTOM = 6;
private static final List<Integer> VALID_SCALE_TYPES = Arrays.asList(SCALE_TYPE_CENTER_CROP, SCALE_TYPE_CENTER_INSIDE, SCALE_TYPE_CUSTOM);
private static final List<Integer> VALID_SCALE_TYPES = Arrays.asList(SCALE_TYPE_CENTER_CROP, SCALE_TYPE_CENTER_INSIDE, SCALE_TYPE_CUSTOM, SCALE_TYPE_FIT_WIDTH, SCALE_TYPE_FIT_HEIGHT, SCALE_TYPE_ORIGINAL_SIZE);
// Bitmap (preview or full image)
private Bitmap bitmap;
@ -2003,12 +2007,20 @@ public class SubsamplingScaleImageView extends View {
private float minScale() {
int vPadding = getPaddingBottom() + getPaddingTop();
int hPadding = getPaddingLeft() + getPaddingRight();
if (minimumScaleType == SCALE_TYPE_CENTER_CROP) {
return Math.max((getWidth() - hPadding) / (float) sWidth(), (getHeight() - vPadding) / (float) sHeight());
} else if (minimumScaleType == SCALE_TYPE_CUSTOM && minScale > 0) {
return minScale;
} else {
return Math.min((getWidth() - hPadding) / (float) sWidth(), (getHeight() - vPadding) / (float) sHeight());
switch (minimumScaleType) {
case SCALE_TYPE_CENTER_INSIDE:
default:
return Math.min((getWidth() - hPadding) / (float) sWidth(), (getHeight() - vPadding) / (float) sHeight());
case SCALE_TYPE_CENTER_CROP:
return Math.max((getWidth() - hPadding) / (float) sWidth(), (getHeight() - vPadding) / (float) sHeight());
case SCALE_TYPE_FIT_WIDTH:
return (getWidth() - hPadding) / (float) sWidth();
case SCALE_TYPE_FIT_HEIGHT:
return (getHeight() - vPadding) / (float) sHeight();
case SCALE_TYPE_ORIGINAL_SIZE:
return 1;
case SCALE_TYPE_CUSTOM:
return minScale;
}
}