From 00f76a09535e6d3700461bcd734aa83d9c8732d7 Mon Sep 17 00:00:00 2001 From: Carl Miller Date: Thu, 27 Nov 2025 21:52:46 -0600 Subject: [PATCH 1/3] Add hold-to-fast-forward feature (2x speed on long press) - Added long press gesture to increase playback speed to 2x while holding - Added setting toggle in Video & Audio settings to enable/disable the feature - Feature is enabled by default - Updated GitHub Actions workflow to build APK and create releases automatically --- .github/workflows/build-release-apk.yml | 44 ++++++++++++--- .../gesture/BasePlayerGestureListener.kt | 53 +++++++++++++++++++ .../newpipe/player/helper/PlayerHelper.java | 5 ++ app/src/main/res/values/settings_keys.xml | 1 + app/src/main/res/values/strings.xml | 2 + app/src/main/res/xml/video_audio_settings.xml | 8 +++ 6 files changed, 106 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build-release-apk.yml b/.github/workflows/build-release-apk.yml index 0fad8e1695e..30b081a6f9e 100644 --- a/.github/workflows/build-release-apk.yml +++ b/.github/workflows/build-release-apk.yml @@ -1,15 +1,21 @@ -name: "Build unsigned release APK on master" +name: "Build and Release APK" on: workflow_dispatch: + push: + branches: + - master + - main + tags: + - 'v*' jobs: release: runs-on: ubuntu-latest + permissions: + contents: write steps: - uses: actions/checkout@v4 - with: - ref: 'master' - uses: actions/setup-java@v4 with: @@ -20,19 +26,43 @@ jobs: - name: "Build release APK" run: ./gradlew assembleRelease --stacktrace - - name: "Rename APK" + - name: "Get version name" + id: version run: | VERSION_NAME="$(jq -r ".elements[0].versionName" "app/build/outputs/apk/release/output-metadata.json")" + echo "version=$VERSION_NAME" >> "$GITHUB_OUTPUT" echo "Version name: $VERSION_NAME" >> "$GITHUB_STEP_SUMMARY" echo '```json' >> "$GITHUB_STEP_SUMMARY" cat "app/build/outputs/apk/release/output-metadata.json" >> "$GITHUB_STEP_SUMMARY" echo >> "$GITHUB_STEP_SUMMARY" echo '```' >> "$GITHUB_STEP_SUMMARY" - # assume there is only one APK in that folder - mv app/build/outputs/apk/release/*.apk "app/build/outputs/apk/release/NewPipe_v$VERSION_NAME.apk" - - name: "Upload APK" + - name: "Rename APK" + run: | + mv app/build/outputs/apk/release/*.apk "app/build/outputs/apk/release/NewPipe_v${{ steps.version.outputs.version }}.apk" + + - name: "Upload APK as artifact" uses: actions/upload-artifact@v4 with: name: app path: app/build/outputs/apk/release/*.apk + + - name: "Create Release" + uses: softprops/action-gh-release@v1 + with: + tag_name: v${{ steps.version.outputs.version }}-${{ github.run_number }} + name: Release v${{ steps.version.outputs.version }} + body: | + ## NewPipe Release v${{ steps.version.outputs.version }} + + ### New Features + - **Hold to Fast-Forward**: Hold anywhere on the player to increase playback speed to 2x. Release to return to normal speed. + - New setting in Video & Audio settings to enable/disable this feature. + + ### Download + Download the APK below and install it on your Android device. + files: app/build/outputs/apk/release/*.apk + draft: false + prerelease: false + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/app/src/main/java/org/schabi/newpipe/player/gesture/BasePlayerGestureListener.kt b/app/src/main/java/org/schabi/newpipe/player/gesture/BasePlayerGestureListener.kt index 0453f297a4f..023c6cdfa71 100644 --- a/app/src/main/java/org/schabi/newpipe/player/gesture/BasePlayerGestureListener.kt +++ b/app/src/main/java/org/schabi/newpipe/player/gesture/BasePlayerGestureListener.kt @@ -9,6 +9,7 @@ import android.view.View import androidx.core.os.postDelayed import org.schabi.newpipe.databinding.PlayerBinding import org.schabi.newpipe.player.Player +import org.schabi.newpipe.player.helper.PlayerHelper import org.schabi.newpipe.player.ui.VideoPlayerUi /** @@ -24,8 +25,59 @@ abstract class BasePlayerGestureListener( protected val player: Player = playerUi.player protected val binding: PlayerBinding = playerUi.binding + // /////////////////////////////////////////////////////////////////// + // Long press (hold) to fast-forward + // /////////////////////////////////////////////////////////////////// + + private var isHoldingForFastForward = false + private var originalPlaybackSpeed = 1.0f + private val longPressHandler: Handler = Handler(Looper.getMainLooper()) + private val longPressRunnable = Runnable { + if (PlayerHelper.isHoldToFastForwardEnabled(player.context) && + player.currentState == Player.STATE_PLAYING && + !playerUi.isSomePopupMenuVisible + ) { + startFastForward() + } + } + + private fun startFastForward() { + if (!isHoldingForFastForward) { + if (DEBUG) { + Log.d(TAG, "startFastForward called") + } + isHoldingForFastForward = true + originalPlaybackSpeed = player.playbackSpeed + player.setPlaybackSpeed(2.0f) + } + } + + private fun stopFastForward() { + if (isHoldingForFastForward) { + if (DEBUG) { + Log.d(TAG, "stopFastForward called, restoring speed to $originalPlaybackSpeed") + } + isHoldingForFastForward = false + player.setPlaybackSpeed(originalPlaybackSpeed) + } + longPressHandler.removeCallbacks(longPressRunnable) + } + override fun onTouch(v: View, event: MotionEvent): Boolean { playerUi.gestureDetector.onTouchEvent(event) + + // Handle long press for fast-forward + when (event.action) { + MotionEvent.ACTION_DOWN -> { + // Start timer for long press detection + longPressHandler.postDelayed(longPressRunnable, LONG_PRESS_DELAY) + } + MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> { + // Stop fast-forward when finger is lifted + stopFastForward() + } + } + return false } @@ -184,5 +236,6 @@ abstract class BasePlayerGestureListener( private const val DOUBLE_TAP = "doubleTap" private const val DOUBLE_TAP_DELAY = 550L + private const val LONG_PRESS_DELAY = 500L } } diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java index c335e9b7c60..6fe7e5a5a86 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java @@ -226,6 +226,11 @@ public static boolean isStartMainPlayerFullscreenEnabled(@NonNull final Context .getBoolean(context.getString(R.string.start_main_player_fullscreen_key), false); } + public static boolean isHoldToFastForwardEnabled(@NonNull final Context context) { + return getPreferences(context) + .getBoolean(context.getString(R.string.hold_to_fast_forward_key), true); + } + public static boolean isAutoQueueEnabled(@NonNull final Context context) { return getPreferences(context) .getBoolean(context.getString(R.string.auto_queue_key), false); diff --git a/app/src/main/res/values/settings_keys.xml b/app/src/main/res/values/settings_keys.xml index 352e4cec120..ad7c0342f68 100644 --- a/app/src/main/res/values/settings_keys.xml +++ b/app/src/main/res/values/settings_keys.xml @@ -24,6 +24,7 @@ screen_brightness_timestamp_key clear_queue_confirmation_key ignore_hardware_media_buttons_key + hold_to_fast_forward_key popup_saved_width popup_saved_x diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 147c88938a9..9b31f43c0c7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -91,6 +91,8 @@ The active player queue will be replaced Ignore hardware media button events Useful, for instance, if you are using a headset with broken physical buttons + Hold to fast-forward + Hold anywhere on the player to increase playback speed to 2x. Release to return to normal speed Show comments Turn off to hide comments Show \'Next\' and \'Similar\' videos diff --git a/app/src/main/res/xml/video_audio_settings.xml b/app/src/main/res/xml/video_audio_settings.xml index 727ce4df40a..b24b034beac 100644 --- a/app/src/main/res/xml/video_audio_settings.xml +++ b/app/src/main/res/xml/video_audio_settings.xml @@ -249,5 +249,13 @@ android:title="@string/ignore_hardware_media_buttons_title" app:singleLineTitle="false" app:iconSpaceReserved="false" /> + + From 4e7da5633ff4cf9a6df1e74dac2578eca6540fba Mon Sep 17 00:00:00 2001 From: Carl Miller Date: Thu, 27 Nov 2025 21:53:09 -0600 Subject: [PATCH 2/3] Update workflow to trigger on dev branch --- .github/workflows/build-release-apk.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build-release-apk.yml b/.github/workflows/build-release-apk.yml index 30b081a6f9e..20070738173 100644 --- a/.github/workflows/build-release-apk.yml +++ b/.github/workflows/build-release-apk.yml @@ -6,6 +6,7 @@ on: branches: - master - main + - dev tags: - 'v*' From 44b3bc686d73ef0514ca83adcc7ab49bfaefa428 Mon Sep 17 00:00:00 2001 From: Carl Miller Date: Thu, 27 Nov 2025 22:10:58 -0600 Subject: [PATCH 3/3] Fix APK installation: add signing and change package name - Changed package ID to org.schabi.newpipe.scrolling to allow coexistence with original NewPipe - Added signing configuration using debug keystore - App will now appear as 'NewPipe Scrolling' on device - Updated GitHub Actions to create keystore for CI builds --- .github/workflows/build-release-apk.yml | 19 ++++++++++++++----- app/build.gradle.kts | 17 ++++++++++++++--- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build-release-apk.yml b/.github/workflows/build-release-apk.yml index 20070738173..73a2dd2dcb7 100644 --- a/.github/workflows/build-release-apk.yml +++ b/.github/workflows/build-release-apk.yml @@ -24,6 +24,11 @@ jobs: java-version: '21' cache: 'gradle' + - name: "Create debug keystore for signing" + run: | + mkdir -p ~/.android + keytool -genkey -v -keystore ~/.android/debug.keystore -storepass android -alias androiddebugkey -keypass android -keyalg RSA -keysize 2048 -validity 10000 -dname "CN=Android Debug,O=Android,C=US" + - name: "Build release APK" run: ./gradlew assembleRelease --stacktrace @@ -40,7 +45,7 @@ jobs: - name: "Rename APK" run: | - mv app/build/outputs/apk/release/*.apk "app/build/outputs/apk/release/NewPipe_v${{ steps.version.outputs.version }}.apk" + mv app/build/outputs/apk/release/*.apk "app/build/outputs/apk/release/NewPipeScrolling_v${{ steps.version.outputs.version }}.apk" - name: "Upload APK as artifact" uses: actions/upload-artifact@v4 @@ -52,16 +57,20 @@ jobs: uses: softprops/action-gh-release@v1 with: tag_name: v${{ steps.version.outputs.version }}-${{ github.run_number }} - name: Release v${{ steps.version.outputs.version }} + name: NewPipe Scrolling v${{ steps.version.outputs.version }} body: | - ## NewPipe Release v${{ steps.version.outputs.version }} + ## NewPipe Scrolling v${{ steps.version.outputs.version }} + + This is a modified version of NewPipe that can be installed alongside the original app. ### New Features - **Hold to Fast-Forward**: Hold anywhere on the player to increase playback speed to 2x. Release to return to normal speed. - New setting in Video & Audio settings to enable/disable this feature. - ### Download - Download the APK below and install it on your Android device. + ### Installation + 1. Download the APK below + 2. Install on your Android device (enable "Install from unknown sources" if needed) + 3. This app will appear as "NewPipe Scrolling" and can coexist with the original NewPipe files: app/build/outputs/apk/release/*.apk draft: false prerelease: false diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 5cf357c7458..4904d91529b 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -36,9 +36,19 @@ android { compileSdk = 36 namespace = "org.schabi.newpipe" + signingConfigs { + create("release") { + // Use debug keystore for unsigned releases that can be installed + storeFile = file("${System.getProperty("user.home")}/.android/debug.keystore") + storePassword = "android" + keyAlias = "androiddebugkey" + keyPassword = "android" + } + } + defaultConfig { - applicationId = "org.schabi.newpipe" - resValue("string", "app_name", "NewPipe") + applicationId = "org.schabi.newpipe.scrolling" + resValue("string", "app_name", "NewPipe Scrolling") minSdk = 21 targetSdk = 35 @@ -72,9 +82,10 @@ android { } release { + signingConfig = signingConfigs.getByName("release") System.getProperty("packageSuffix")?.let { suffix -> applicationIdSuffix = suffix - resValue("string", "app_name", "NewPipe $suffix") + resValue("string", "app_name", "NewPipe Scrolling $suffix") } isMinifyEnabled = true isShrinkResources = false // disabled to fix F-Droid"s reproducible build