diff --git a/app/build.gradle b/app/build.gradle index c0ff1dab..caaf0e77 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -136,19 +136,15 @@ dependencies { implementation 'org.tukaani:xz:1.10' // 7-Zip-JBinding-4Android 16.02-2.03+ API21 - //implementation 'com.github.omicronapps:7-Zip-JBinding-4Android:Release-16.02-2.02' - //implementation 'com.github.edeso:7-Zip-JBinding-4Android-test:2025.01.25' - //implementation 'com.github.lings03:7-Zip-JBinding-4Android:2024.12' - //implementation 'com.sorrowblue.sevenzipjbinding:7-Zip-JBinding-4Android:16.02-2.03' implementation 'com.github.edeso:7-Zip-JBinding-4Android:2025.01.26-1' implementation 'com.github.luben:zstd-jni:1.5.6-9@aar' implementation 'org.brotli:dec:0.1.2' // jp2-android 1.0.4+ API21, gemalto aar vanished from jcenter - //implementation 'com.gemalto.jp2:jp2-android:1.0.3' - //implementation files('lib/jp2-android-1.0.3.aar') implementation 'dev.keiji.jp2:jp2-android:1.0.5' + implementation "androidx.preference:preference:1.2.1" + // workaround fix 'Duplicate class kotlin.collections.jdk8.CollectionsJDK8Kt found in modules ...' //implementation(platform("org.jetbrains.kotlin:kotlin-bom:2.0.0")) diff --git a/app/src/main/java/com/nkanaev/comics/activity/MainActivity.java b/app/src/main/java/com/nkanaev/comics/activity/MainActivity.java index b138248b..63aa2e06 100644 --- a/app/src/main/java/com/nkanaev/comics/activity/MainActivity.java +++ b/app/src/main/java/com/nkanaev/comics/activity/MainActivity.java @@ -19,6 +19,7 @@ import com.nkanaev.comics.fragment.AboutFragment; import com.nkanaev.comics.fragment.BrowserFragment; import com.nkanaev.comics.fragment.LibraryFragment; +import com.nkanaev.comics.fragment.PreferencesFragment; import com.nkanaev.comics.managers.LocalCoverHandler; import com.nkanaev.comics.managers.Scanner; import com.nkanaev.comics.managers.Utils; @@ -204,6 +205,9 @@ public boolean onNavigationItemSelected(MenuItem menuItem) { case R.id.drawer_menu_about: setFragment(new AboutFragment()); break; + case R.id.drawer_menu_preferences: + setFragment(new PreferencesFragment()); + break; } mCurrentNavItem = menuItem.getItemId(); diff --git a/app/src/main/java/com/nkanaev/comics/activity/ReaderActivity.java b/app/src/main/java/com/nkanaev/comics/activity/ReaderActivity.java index 14dbb726..ec27eb47 100644 --- a/app/src/main/java/com/nkanaev/comics/activity/ReaderActivity.java +++ b/app/src/main/java/com/nkanaev/comics/activity/ReaderActivity.java @@ -9,6 +9,10 @@ import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; +import androidx.core.view.OnApplyWindowInsetsListener; +import androidx.core.view.ViewCompat; +import androidx.core.view.WindowInsetsCompat; +import androidx.core.graphics.Insets; import androidx.fragment.app.Fragment; import com.nkanaev.comics.BuildConfig; import com.nkanaev.comics.R; @@ -46,8 +50,9 @@ public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.layout_reader); - Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar_reader); - setSupportActionBar(toolbar); + initToolBar(); + initBottomNavigation(); + initActionBar(); if (savedInstanceState == null) { if (Intent.ACTION_VIEW.equals(getIntent().getAction())) { @@ -66,14 +71,45 @@ public void onCreate(Bundle savedInstanceState) { setFragment(fragment); } } + } + private void initToolBar() { + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar_reader); + setSupportActionBar(toolbar); + + ViewCompat.setOnApplyWindowInsetsListener(toolbar, new OnApplyWindowInsetsListener() { + public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) { + Insets systemBarInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars()); + v.setPadding(0, systemBarInsets.top, 0, 0); + + return WindowInsetsCompat.CONSUMED; + } + }); + } + + private void initBottomNavigation() { + View navWrapper = findViewById(R.id.reader_bottom_nav_wrapper); + + ViewCompat.setOnApplyWindowInsetsListener(navWrapper, new OnApplyWindowInsetsListener() { + public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) { + Insets systemBarInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars()); + v.setPadding(0, 0, 0, systemBarInsets.bottom); + + return WindowInsetsCompat.CONSUMED; + } + }); + } + + private void initActionBar() { ActionBar actionBar = getSupportActionBar(); - if (actionBar != null) { - actionBar.setDisplayHomeAsUpEnabled(true); - actionBar.setDisplayShowCustomEnabled(true); - actionBar.setCustomView(R.layout.action_bar_title_layout); - actionBar.setTitle(""); + if (actionBar == null) { + return; } + + actionBar.setDisplayHomeAsUpEnabled(true); + actionBar.setDisplayShowCustomEnabled(true); + actionBar.setCustomView(R.layout.action_bar_title_layout); + actionBar.setTitle(""); } @Override diff --git a/app/src/main/java/com/nkanaev/comics/fragment/PreferencesFragment.java b/app/src/main/java/com/nkanaev/comics/fragment/PreferencesFragment.java new file mode 100644 index 00000000..6d443a92 --- /dev/null +++ b/app/src/main/java/com/nkanaev/comics/fragment/PreferencesFragment.java @@ -0,0 +1,21 @@ +package com.nkanaev.comics.fragment; + +import android.os.Bundle; + +import androidx.preference.PreferenceFragmentCompat; + +import com.nkanaev.comics.R; + +public class PreferencesFragment extends PreferenceFragmentCompat { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + getActivity().setTitle(R.string.menu_preferences); + } + + @Override + public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { + setPreferencesFromResource(R.xml.preferences, rootKey); + } +} diff --git a/app/src/main/java/com/nkanaev/comics/fragment/ReaderFragment.java b/app/src/main/java/com/nkanaev/comics/fragment/ReaderFragment.java index 0780530d..dd6a195e 100644 --- a/app/src/main/java/com/nkanaev/comics/fragment/ReaderFragment.java +++ b/app/src/main/java/com/nkanaev/comics/fragment/ReaderFragment.java @@ -34,6 +34,7 @@ import androidx.interpolator.view.animation.FastOutSlowInInterpolator; import androidx.viewpager.widget.PagerAdapter; import androidx.viewpager.widget.ViewPager; +import androidx.preference.PreferenceManager; import com.nkanaev.comics.BuildConfig; import com.nkanaev.comics.Constants; import com.nkanaev.comics.MainApplication; @@ -58,6 +59,7 @@ import java.io.IOException; import java.io.InputStream; import java.lang.ref.WeakReference; +import java.lang.IllegalStateException; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.text.NumberFormat; @@ -75,7 +77,7 @@ public class ReaderFragment extends Fragment implements View.OnTouchListener { public static final String STATE_PAGE_ROTATIONS = "STATE_PAGE_ROTATIONS"; private ComicViewPager mViewPager; - private View mPageNavLayout; + private View mNavigationOverlay; private SeekBar mPageSeekBar; private TextView mPageNavTextView; private TextView mPageInfoTextView; @@ -250,7 +252,7 @@ public void run() { .addRequestHandler(mComicHandler) .build(); - mGestureDetector = new GestureDetector(getActivity(), new MyTouchListener()); + initGestureDetector(); SharedPreferences preferences = MainApplication.getPreferences(); int viewModeInt = preferences.getInt( @@ -265,10 +267,10 @@ public void run() { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { final View view = inflater.inflate(R.layout.fragment_reader, container, false); - mPageNavLayout = getActivity().findViewById(R.id.pageNavLayout); + mNavigationOverlay = getActivity().findViewById(R.id.navigation_overlay); // setup seekbar - mPageSeekBar = (SeekBar) mPageNavLayout.findViewById(R.id.pageSeekBar); + mPageSeekBar = (SeekBar) mNavigationOverlay.findViewById(R.id.pageSeekBar); mPageSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { @@ -292,12 +294,12 @@ public void onStopTrackingTouch(SeekBar seekBar) { }); updateSeekBar(); - mPageNavTextView = (TextView) mPageNavLayout.findViewById(R.id.pageNavTextView); + mPageNavTextView = (TextView) mNavigationOverlay.findViewById(R.id.pageNavTextView); mPageNavTextView.setText(""); // strip dummy text // setup page info button - mPageInfoButton = mPageNavLayout.findViewById(R.id.pageInfoButton); - mPageInfoTextView = mPageNavLayout.findViewById(R.id.pageInfoTextView); + mPageInfoButton = mNavigationOverlay.findViewById(R.id.pageInfoButton); + mPageInfoTextView = mNavigationOverlay.findViewById(R.id.pageInfoTextView); mPageInfoTextView.setText(""); // strip dummy text setPageInfoShown(mIsPageInfoShown); View.OnClickListener ocl = new View.OnClickListener() { @@ -840,83 +842,132 @@ public void onClick(View v) { } } - private class MyTouchListener extends GestureDetector.SimpleOnGestureListener { - /** - * switch menus and pageseekbar on/off on long press anywhere - * - * @param e The initial on down motion event that started the longpress. - */ - @Override - public void onLongPress(MotionEvent e) { - // always switch of menus first - if (!isFullscreen()) { - setFullscreen(true); - return; - } + private class NavigationOverlayTouchListener extends GestureDetector.SimpleOnGestureListener { + private final float THRESHOLD_MAX = (float) 0.3; + private final float THRESHOLD_MIN = (float) 0.1; - float x = e.getX(); - float y = e.getY(); - float width = (float) mViewPager.getWidth(); - float height = (float) mViewPager.getHeight(); + protected float mActivationThreshold; - // hotspot only 60% centered - int div = 10; - if (x < width / div * 2 || x > width / div * 8 - || y < height / div * 2 || y > height / div * 8) - return; + public NavigationOverlayTouchListener() { + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getContext()); + int numerator = preferences.getInt( + getString(R.string.preferences_reader_nav_activation_threshold_key), + 0); - boolean fullScreen = !isFullscreen(); - setFullscreen(fullScreen); + float diff = THRESHOLD_MAX - THRESHOLD_MIN; + float percentage = numerator / (float) 100.0; + mActivationThreshold = THRESHOLD_MIN + (diff * percentage); } - /** - * single taps on left/ride side switch to prev/next page - * - * @param e The down motion event of the single-tap. - * @return boolean true if the event is consumed, else false - */ - @Override - public boolean onSingleTapConfirmed(MotionEvent e) { + protected boolean handleEvent(MotionEvent e) { float x = e.getX(); + float width = (float) mViewPager.getWidth(); - // tap left side - if (x < (float) mViewPager.getWidth() / 10 * 3) { - if (mIsLeftToRight) { - if (getCurrentPage() == 1) - hitBeginning(); - else - setCurrentPage(getCurrentPage() - 1); + boolean isLeftTouch = isLeftTouch(x, width, mActivationThreshold); + boolean isRightTouch = isRightTouch(x, width, mActivationThreshold); + + if (isLeftTouch || isRightTouch) { + handlePageTurning(isLeftTouch); + return true; + } else { + if (!isFullscreen()) { + setFullscreen(true); + return true; } else { - if (getCurrentPage() == mPageCount) - hitEnding(); - else - setCurrentPage(getCurrentPage() + 1); + setFullscreen(false); + return false; } - return true; } - // tap right side - else if (x > (float) mViewPager.getWidth() / 10 * 7) { + } + + protected void handlePageTurning(boolean isLeftTouch) { + if (isLeftTouch) { if (mIsLeftToRight) { - if (getCurrentPage() == mPageCount) - hitEnding(); - else - setCurrentPage(getCurrentPage() + 1); + goToPreviousPage(); } else { - if (getCurrentPage() == 1) - hitBeginning(); - else - setCurrentPage(getCurrentPage() - 1); + goToNextPage(); + } + } else { + if (mIsLeftToRight) { + goToNextPage(); + } else { + goToPreviousPage(); } - return true; } + } - // switch of menus if not navigating - if (!isFullscreen()) { - setFullscreen(true); - return true; + protected void goToPreviousPage() { + if (getCurrentPage() == 1) { + hitBeginning(); + } else { + setCurrentPage(getCurrentPage() - 1); + } + } + + protected void goToNextPage() { + if (getCurrentPage() == mPageCount) { + hitEnding(); + } else { + setCurrentPage(getCurrentPage() + 1); } + } - return false; + protected boolean isInnerTouch(float point, float length, float percentage) { + return !isOuterTouch(point, length, percentage); + } + + protected boolean isOuterTouch(float point, float length, float percentage) { + return isLeftTouch(point, length, percentage) || isRightTouch(point, length, percentage); + } + + protected boolean isLeftTouch(float point, float length, float percentage) { + return point < length * percentage; + } + + protected boolean isRightTouch(float point, float length, float percentage) { + return point > length * (1 - percentage); + } + } + + private class SingleTapUpNavigationOverlayTouchListener extends NavigationOverlayTouchListener { + @Override + public boolean onSingleTapUp(MotionEvent e) { + return handleEvent(e); + } + } + + private class SingleTapConfirmNavigationOverlayTouchListener extends NavigationOverlayTouchListener { + @Override + public boolean onSingleTapConfirmed(MotionEvent e) { + return handleEvent(e); + } + } + + private class LongPressNavigationOverlayTouchListener extends NavigationOverlayTouchListener { + @Override + public void onLongPress(MotionEvent e) { + handleEvent(e); + } + + @Override + public boolean onSingleTapConfirmed(MotionEvent e) { + float x = e.getX(); + float width = (float) mViewPager.getWidth(); + + boolean isLeftTouch = isLeftTouch(x, width, mActivationThreshold); + boolean isRightTouch = isRightTouch(x, width, mActivationThreshold); + + if (isLeftTouch || isRightTouch) { + handlePageTurning(isLeftTouch); + return true; + } else { + if (!isFullscreen()) { + setFullscreen(true); + return true; + } else { + return false; + } + } } } @@ -966,9 +1017,7 @@ private void setFullscreen(boolean fullscreen) { if (fullscreen) { if (actionBar != null) actionBar.hide(); - mPageNavLayout.setVisibility(View.INVISIBLE); - -// wic.hide(WindowInsetsCompat.Type.systemBars()); + mNavigationOverlay.setVisibility(View.INVISIBLE); int flag = View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN @@ -980,20 +1029,7 @@ private void setFullscreen(boolean fullscreen) { } decorView.setSystemUiVisibility(flag); - // replaced by value in ReaderTheme style - /* - // allow full screen over display cutouts/holes (since Android 9) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - Window w = getActivity().getWindow(); - WindowManager.LayoutParams layoutParams = w.getAttributes(); - layoutParams.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; - w.setAttributes(layoutParams); - } - */ - } else { -// wic.show(WindowInsetsCompat.Type.systemBars()); - int flag = View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; if (Utils.isKitKatOrLater()) { @@ -1004,7 +1040,7 @@ private void setFullscreen(boolean fullscreen) { mPageSeekBar.setMax(mPageCount - 1); if (actionBar != null) actionBar.show(); - mPageNavLayout.setVisibility(View.VISIBLE); + mNavigationOverlay.setVisibility(View.VISIBLE); // WORKAROUND: // status bar & navigation bar background won't show, being transparent, @@ -1165,6 +1201,30 @@ public void run() { } } + private void initGestureDetector() { + final String prefKeySingleTap = getString(R.string.preferences_reader_nav_overlay_activation_type_single_tap); + final String prefKeySingleTapConfirmed = getString(R.string.preferences_reader_nav_overlay_activation_type_single_tap_confirmed); + final String prefKeyLongPress = getString(R.string.preferences_reader_nav_overlay_activation_type_long_press); + + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getContext()); + String activationType = preferences.getString( + getString(R.string.preferences_reader_nav_overlay_activation_type_key), + getString(R.string.preferences_reader_nav_overlay_activation_type_long_press)); + + GestureDetector.SimpleOnGestureListener listener; + if (activationType.equals(prefKeySingleTap)) { + listener = new SingleTapUpNavigationOverlayTouchListener(); + } else if (activationType.equals(prefKeySingleTapConfirmed)) { + listener = new SingleTapConfirmNavigationOverlayTouchListener(); + } else if (activationType.equals(prefKeyLongPress)) { + listener = new LongPressNavigationOverlayTouchListener(); + } else { + throw new IllegalStateException(String.format("Unknown activationType %s", activationType)); + } + + mGestureDetector = new GestureDetector(getActivity(), listener); + } + @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { if (requestCode == 1) { diff --git a/app/src/main/res/drawable/ic_cogwheel_24.xml b/app/src/main/res/drawable/ic_cogwheel_24.xml new file mode 100644 index 00000000..4dd04cca --- /dev/null +++ b/app/src/main/res/drawable/ic_cogwheel_24.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/layout_main.xml b/app/src/main/res/layout/layout_main.xml index dda10919..87ff6afc 100644 --- a/app/src/main/res/layout/layout_main.xml +++ b/app/src/main/res/layout/layout_main.xml @@ -5,6 +5,7 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/drawer_layout" + android:fitsSystemWindows="true" android:layout_width="fill_parent" android:layout_height="fill_parent"> + android:fitsSystemWindows="false"> + android:background="@color/reader_nav_bg"> + \ No newline at end of file diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml new file mode 100644 index 00000000..4918bb32 --- /dev/null +++ b/app/src/main/res/values/arrays.xml @@ -0,0 +1,8 @@ + + + + @string/preferences_reader_nav_overlay_activation_type_long_press + @string/preferences_reader_nav_overlay_activation_type_single_tap + @string/preferences_reader_nav_overlay_activation_type_single_tap_confirmed + + diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 557c4561..564db7fd 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -26,6 +26,8 @@ @color/grey50 @color/white_trans75 + @color/darkest_trans50 + #c84a59 #b44350 #9e3b46 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a1271dd4..5a91b1c0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -50,6 +50,7 @@ Library Browser About + Preferences Recent All @@ -79,4 +80,14 @@ Your collection seems empty. Click \"folder\" icon to select library directory. Missing file. Please update the library + + Reader navigation overlay activation type + reader_nav_overlay_activation_type + Single tap + Single tap allowing double tap to zoom(slower) + Long tap + + Left/Right tap area to turn page + preferences_reader_nav_activation_threshold_key + 10\% ~ 30\% diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml new file mode 100644 index 00000000..cf4a49bd --- /dev/null +++ b/app/src/main/res/xml/preferences.xml @@ -0,0 +1,14 @@ + + + + + diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..a4b76b95 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 4e089f6c..d7104778 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,8 @@ -#Fri Sep 01 02:53:16 CEST 2023 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionSha256Sum=8d97a97984f6cbd2b85fe4c60a743440a347544bf18818048e611f5288d46c94 distributionUrl=https\://services.gradle.org/distributions/gradle-8.12.1-bin.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 00000000..f3b75f3b --- /dev/null +++ b/gradlew @@ -0,0 +1,251 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# 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 +# +# https://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. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 00000000..9b42019c --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,94 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega