Loading app/src/main/java/app/fedilab/android/mastodon/ui/fragment/media/FragmentMedia.java +102 −0 Original line number Diff line number Diff line Loading @@ -26,7 +26,10 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.PopupMenu; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; Loading @@ -37,6 +40,7 @@ import androidx.fragment.app.Fragment; import androidx.lifecycle.ViewModelProvider; import androidx.media3.common.MediaItem; import androidx.media3.common.PlaybackException; import androidx.media3.common.PlaybackParameters; import androidx.media3.common.Player; import androidx.media3.common.util.UnstableApi; import androidx.media3.datasource.DataSource; Loading @@ -45,6 +49,8 @@ import androidx.media3.exoplayer.ExoPlayer; import androidx.media3.exoplayer.source.ProgressiveMediaSource; import androidx.preference.PreferenceManager; import com.google.android.material.slider.Slider; import com.bumptech.glide.Glide; import com.bumptech.glide.request.target.CustomTarget; import com.bumptech.glide.request.transition.Transition; Loading Loading @@ -80,6 +86,8 @@ public class FragmentMedia extends Fragment { private int mediaVideoTranslateAccessibilityActionId = 0; private boolean visible = false; private float currentVolume = 1.0f; private float currentSpeed = 1.0f; public FragmentMedia() { } Loading Loading @@ -348,6 +356,7 @@ public class FragmentMedia extends Fragment { } binding.mediaVideo.setPlayer(player); binding.controls.setPlayer(player); setupExtraControls(); binding.loader.setVisibility(View.GONE); binding.mediaPicture.setVisibility(View.GONE); player.setMediaSource(videoSource); Loading Loading @@ -466,6 +475,99 @@ public class FragmentMedia extends Fragment { }); } private void setupExtraControls() { if (binding == null || player == null) { return; } ImageButton volumeIcon = binding.controls.findViewById(R.id.exo_volume_icon); Slider volumeSlider = binding.controls.findViewById(R.id.exo_volume_slider); TextView playbackSpeed = binding.controls.findViewById(R.id.exo_playback_speed); if (volumeSlider != null) { volumeSlider.setValue(currentVolume * 100); volumeSlider.addOnChangeListener((slider, value, fromUser) -> { if (fromUser && player != null) { currentVolume = value / 100f; player.setVolume(currentVolume); updateVolumeIcon(volumeIcon); } }); } if (volumeIcon != null) { volumeIcon.setOnClickListener(v -> { if (player == null || volumeSlider == null) { return; } if (currentVolume > 0) { currentVolume = 0f; } else { currentVolume = 1.0f; } player.setVolume(currentVolume); volumeSlider.setValue(currentVolume * 100); updateVolumeIcon(volumeIcon); }); } if (playbackSpeed != null) { updateSpeedText(playbackSpeed); playbackSpeed.setOnClickListener(v -> showSpeedMenu(playbackSpeed)); } } private void updateVolumeIcon(ImageButton volumeIcon) { if (volumeIcon == null) { return; } if (currentVolume == 0) { volumeIcon.setImageResource(R.drawable.ic_baseline_volume_mute_24); } else { volumeIcon.setImageResource(R.drawable.ic_baseline_volume_up_24); } } private void updateSpeedText(TextView playbackSpeed) { if (playbackSpeed == null) { return; } if (currentSpeed == 1.0f) { playbackSpeed.setText(getString(R.string.normal)); } else if (currentSpeed == (int) currentSpeed) { playbackSpeed.setText(String.format("%dx", (int) currentSpeed)); } else { playbackSpeed.setText(String.format("%sx", currentSpeed)); } } private void showSpeedMenu(TextView playbackSpeed) { if (!isAdded() || getContext() == null) { return; } PopupMenu popup = new PopupMenu(requireContext(), playbackSpeed); float[] speeds = {0.25f, 0.5f, 0.75f, 1.0f, 1.25f, 1.5f, 1.75f, 2.0f}; for (int i = 0; i < speeds.length; i++) { String label; if (speeds[i] == 1.0f) { label = getString(R.string.normal); } else if (speeds[i] == (int) speeds[i]) { label = String.format("%dx", (int) speeds[i]); } else { label = String.format("%sx", speeds[i]); } popup.getMenu().add(0, i, i, label); } popup.setOnMenuItemClickListener(item -> { int index = item.getItemId(); if (index >= 0 && index < speeds.length && player != null) { currentSpeed = speeds[index]; player.setPlaybackParameters(new PlaybackParameters(currentSpeed)); updateSpeedText(playbackSpeed); } return true; }); popup.show(); } private void enableSliding(boolean enable) { if (enable && !swipeEnabled) { Loading app/src/main/res/drawables/mastodon/drawable/ic_baseline_volume_up_24.xml 0 → 100644 +11 −0 Original line number Diff line number Diff line <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:autoMirrored="true" android:tint="?attr/colorControlNormal" android:viewportWidth="24" android:viewportHeight="24"> <path android:fillColor="@android:color/white" android:pathData="M3,9v6h4l5,5V4L7,9H3zM16.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v8.05c1.48,-0.73 2.5,-2.25 2.5,-4.02zM14,3.23v2.06c2.89,0.86 5,3.54 5,6.71s-2.11,5.85 -5,6.71v2.06c4.01,-0.91 7,-4.49 7,-8.77s-2.99,-7.86 -7,-8.77z" /> </vector> app/src/main/res/layouts/mastodon/layout/player_control_view.xml +47 −1 Original line number Diff line number Diff line Loading @@ -48,4 +48,50 @@ android:textSize="12sp" /> </androidx.appcompat.widget.LinearLayoutCompat> <androidx.appcompat.widget.LinearLayoutCompat android:id="@+id/extra_controls" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="4dp" android:gravity="center_vertical" android:orientation="horizontal" android:paddingStart="8dp" android:paddingEnd="8dp"> <ImageButton android:id="@+id/exo_volume_icon" android:layout_width="32dp" android:layout_height="32dp" android:background="?attr/selectableItemBackgroundBorderless" android:contentDescription="@string/volume" android:src="@drawable/ic_baseline_volume_up_24" app:tint="?colorPrimary" /> <com.google.android.material.slider.Slider android:id="@+id/exo_volume_slider" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:valueFrom="0" android:valueTo="100" android:value="100" android:contentDescription="@string/volume" app:thumbColor="?colorPrimary" app:trackColorActive="?colorPrimary" app:trackColorInactive="?colorSecondaryContainer" /> <TextView android:id="@+id/exo_playback_speed" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="8dp" android:background="?attr/selectableItemBackgroundBorderless" android:padding="8dp" android:text="1x" android:textColor="?colorPrimary" android:textSize="14sp" android:textStyle="bold" /> </androidx.appcompat.widget.LinearLayoutCompat> </androidx.appcompat.widget.LinearLayoutCompat> app/src/main/res/values/strings.xml +1 −0 Original line number Diff line number Diff line Loading @@ -41,6 +41,7 @@ <string name="open_with">Open with</string> <string name="validate">Validate</string> <string name="media">Media</string> <string name="volume">Volume</string> <string name="plus_media">+%d media</string> <string name="share_with">Share with</string> <string name="shared_via">Shared via Fedilab</string> Loading Loading
app/src/main/java/app/fedilab/android/mastodon/ui/fragment/media/FragmentMedia.java +102 −0 Original line number Diff line number Diff line Loading @@ -26,7 +26,10 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.PopupMenu; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; Loading @@ -37,6 +40,7 @@ import androidx.fragment.app.Fragment; import androidx.lifecycle.ViewModelProvider; import androidx.media3.common.MediaItem; import androidx.media3.common.PlaybackException; import androidx.media3.common.PlaybackParameters; import androidx.media3.common.Player; import androidx.media3.common.util.UnstableApi; import androidx.media3.datasource.DataSource; Loading @@ -45,6 +49,8 @@ import androidx.media3.exoplayer.ExoPlayer; import androidx.media3.exoplayer.source.ProgressiveMediaSource; import androidx.preference.PreferenceManager; import com.google.android.material.slider.Slider; import com.bumptech.glide.Glide; import com.bumptech.glide.request.target.CustomTarget; import com.bumptech.glide.request.transition.Transition; Loading Loading @@ -80,6 +86,8 @@ public class FragmentMedia extends Fragment { private int mediaVideoTranslateAccessibilityActionId = 0; private boolean visible = false; private float currentVolume = 1.0f; private float currentSpeed = 1.0f; public FragmentMedia() { } Loading Loading @@ -348,6 +356,7 @@ public class FragmentMedia extends Fragment { } binding.mediaVideo.setPlayer(player); binding.controls.setPlayer(player); setupExtraControls(); binding.loader.setVisibility(View.GONE); binding.mediaPicture.setVisibility(View.GONE); player.setMediaSource(videoSource); Loading Loading @@ -466,6 +475,99 @@ public class FragmentMedia extends Fragment { }); } private void setupExtraControls() { if (binding == null || player == null) { return; } ImageButton volumeIcon = binding.controls.findViewById(R.id.exo_volume_icon); Slider volumeSlider = binding.controls.findViewById(R.id.exo_volume_slider); TextView playbackSpeed = binding.controls.findViewById(R.id.exo_playback_speed); if (volumeSlider != null) { volumeSlider.setValue(currentVolume * 100); volumeSlider.addOnChangeListener((slider, value, fromUser) -> { if (fromUser && player != null) { currentVolume = value / 100f; player.setVolume(currentVolume); updateVolumeIcon(volumeIcon); } }); } if (volumeIcon != null) { volumeIcon.setOnClickListener(v -> { if (player == null || volumeSlider == null) { return; } if (currentVolume > 0) { currentVolume = 0f; } else { currentVolume = 1.0f; } player.setVolume(currentVolume); volumeSlider.setValue(currentVolume * 100); updateVolumeIcon(volumeIcon); }); } if (playbackSpeed != null) { updateSpeedText(playbackSpeed); playbackSpeed.setOnClickListener(v -> showSpeedMenu(playbackSpeed)); } } private void updateVolumeIcon(ImageButton volumeIcon) { if (volumeIcon == null) { return; } if (currentVolume == 0) { volumeIcon.setImageResource(R.drawable.ic_baseline_volume_mute_24); } else { volumeIcon.setImageResource(R.drawable.ic_baseline_volume_up_24); } } private void updateSpeedText(TextView playbackSpeed) { if (playbackSpeed == null) { return; } if (currentSpeed == 1.0f) { playbackSpeed.setText(getString(R.string.normal)); } else if (currentSpeed == (int) currentSpeed) { playbackSpeed.setText(String.format("%dx", (int) currentSpeed)); } else { playbackSpeed.setText(String.format("%sx", currentSpeed)); } } private void showSpeedMenu(TextView playbackSpeed) { if (!isAdded() || getContext() == null) { return; } PopupMenu popup = new PopupMenu(requireContext(), playbackSpeed); float[] speeds = {0.25f, 0.5f, 0.75f, 1.0f, 1.25f, 1.5f, 1.75f, 2.0f}; for (int i = 0; i < speeds.length; i++) { String label; if (speeds[i] == 1.0f) { label = getString(R.string.normal); } else if (speeds[i] == (int) speeds[i]) { label = String.format("%dx", (int) speeds[i]); } else { label = String.format("%sx", speeds[i]); } popup.getMenu().add(0, i, i, label); } popup.setOnMenuItemClickListener(item -> { int index = item.getItemId(); if (index >= 0 && index < speeds.length && player != null) { currentSpeed = speeds[index]; player.setPlaybackParameters(new PlaybackParameters(currentSpeed)); updateSpeedText(playbackSpeed); } return true; }); popup.show(); } private void enableSliding(boolean enable) { if (enable && !swipeEnabled) { Loading
app/src/main/res/drawables/mastodon/drawable/ic_baseline_volume_up_24.xml 0 → 100644 +11 −0 Original line number Diff line number Diff line <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:autoMirrored="true" android:tint="?attr/colorControlNormal" android:viewportWidth="24" android:viewportHeight="24"> <path android:fillColor="@android:color/white" android:pathData="M3,9v6h4l5,5V4L7,9H3zM16.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v8.05c1.48,-0.73 2.5,-2.25 2.5,-4.02zM14,3.23v2.06c2.89,0.86 5,3.54 5,6.71s-2.11,5.85 -5,6.71v2.06c4.01,-0.91 7,-4.49 7,-8.77s-2.99,-7.86 -7,-8.77z" /> </vector>
app/src/main/res/layouts/mastodon/layout/player_control_view.xml +47 −1 Original line number Diff line number Diff line Loading @@ -48,4 +48,50 @@ android:textSize="12sp" /> </androidx.appcompat.widget.LinearLayoutCompat> <androidx.appcompat.widget.LinearLayoutCompat android:id="@+id/extra_controls" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="4dp" android:gravity="center_vertical" android:orientation="horizontal" android:paddingStart="8dp" android:paddingEnd="8dp"> <ImageButton android:id="@+id/exo_volume_icon" android:layout_width="32dp" android:layout_height="32dp" android:background="?attr/selectableItemBackgroundBorderless" android:contentDescription="@string/volume" android:src="@drawable/ic_baseline_volume_up_24" app:tint="?colorPrimary" /> <com.google.android.material.slider.Slider android:id="@+id/exo_volume_slider" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:valueFrom="0" android:valueTo="100" android:value="100" android:contentDescription="@string/volume" app:thumbColor="?colorPrimary" app:trackColorActive="?colorPrimary" app:trackColorInactive="?colorSecondaryContainer" /> <TextView android:id="@+id/exo_playback_speed" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="8dp" android:background="?attr/selectableItemBackgroundBorderless" android:padding="8dp" android:text="1x" android:textColor="?colorPrimary" android:textSize="14sp" android:textStyle="bold" /> </androidx.appcompat.widget.LinearLayoutCompat> </androidx.appcompat.widget.LinearLayoutCompat>
app/src/main/res/values/strings.xml +1 −0 Original line number Diff line number Diff line Loading @@ -41,6 +41,7 @@ <string name="open_with">Open with</string> <string name="validate">Validate</string> <string name="media">Media</string> <string name="volume">Volume</string> <string name="plus_media">+%d media</string> <string name="share_with">Share with</string> <string name="shared_via">Shared via Fedilab</string> Loading