Add local file picker to ConfigurationActivity

PiperOrigin-RevId: 480349627
This commit is contained in:
Googler 2022-10-11 14:21:12 +00:00 committed by Marc Baechinger
parent 0468b5ab72
commit b515e0bd7f
3 changed files with 112 additions and 28 deletions

View File

@ -18,9 +18,11 @@ package androidx.media3.demo.transformer;
import static androidx.media3.common.util.Assertions.checkNotNull; import static androidx.media3.common.util.Assertions.checkNotNull;
import static androidx.media3.common.util.Assertions.checkState; import static androidx.media3.common.util.Assertions.checkState;
import android.Manifest;
import android.app.Activity; import android.app.Activity;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.view.View; import android.view.View;
@ -29,9 +31,14 @@ import android.widget.Button;
import android.widget.CheckBox; import android.widget.CheckBox;
import android.widget.Spinner; import android.widget.Spinner;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast;
import androidx.activity.result.ActivityResult;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.media3.common.C; import androidx.media3.common.C;
import androidx.media3.common.MimeTypes; import androidx.media3.common.MimeTypes;
import androidx.media3.common.util.Util; import androidx.media3.common.util.Util;
@ -78,7 +85,8 @@ public final class ConfigurationActivity extends AppCompatActivity {
public static final int COLOR_FILTER_GRAYSCALE = 0; public static final int COLOR_FILTER_GRAYSCALE = 0;
public static final int COLOR_FILTER_INVERTED = 1; public static final int COLOR_FILTER_INVERTED = 1;
public static final int COLOR_FILTER_SEPIA = 2; public static final int COLOR_FILTER_SEPIA = 2;
private static final String[] INPUT_URIS = { public static final int FILE_PERMISSION_REQUEST_CODE = 1;
private static final String[] PRESET_FILE_URIS = {
"https://storage.googleapis.com/exoplayer-test-media-1/mp4/android-screens-10s.mp4", "https://storage.googleapis.com/exoplayer-test-media-1/mp4/android-screens-10s.mp4",
"https://storage.googleapis.com/exoplayer-test-media-0/android-block-1080-hevc.mp4", "https://storage.googleapis.com/exoplayer-test-media-0/android-block-1080-hevc.mp4",
"https://html5demos.com/assets/dizzy.mp4", "https://html5demos.com/assets/dizzy.mp4",
@ -93,7 +101,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
"https://storage.googleapis.com/exoplayer-test-media-1/gen/screens/dash-vod-single-segment/manifest-baseline.mpd", "https://storage.googleapis.com/exoplayer-test-media-1/gen/screens/dash-vod-single-segment/manifest-baseline.mpd",
"https://storage.googleapis.com/exoplayer-test-media-1/mp4/samsung-s21-hdr-hdr10.mp4", "https://storage.googleapis.com/exoplayer-test-media-1/mp4/samsung-s21-hdr-hdr10.mp4",
}; };
private static final String[] URI_DESCRIPTIONS = { // same order as INPUT_URIS private static final String[] PRESET_FILE_URI_DESCRIPTIONS = { // same order as PRESET_FILE_URIS
"720p H264 video and AAC audio", "720p H264 video and AAC audio",
"1080p H265 video and AAC audio", "1080p H265 video and AAC audio",
"360p H264 video and AAC audio", "360p H264 video and AAC audio",
@ -129,7 +137,9 @@ public final class ConfigurationActivity extends AppCompatActivity {
private static final String SAME_AS_INPUT_OPTION = "same as input"; private static final String SAME_AS_INPUT_OPTION = "same as input";
private static final float HALF_DIAGONAL = 1f / (float) Math.sqrt(2); private static final float HALF_DIAGONAL = 1f / (float) Math.sqrt(2);
private @MonotonicNonNull Button selectFileButton; private @MonotonicNonNull ActivityResultLauncher<Intent> localFilePickerLauncher;
private @MonotonicNonNull Button selectPresetFileButton;
private @MonotonicNonNull Button selectLocalFileButton;
private @MonotonicNonNull TextView selectedFileTextView; private @MonotonicNonNull TextView selectedFileTextView;
private @MonotonicNonNull CheckBox removeAudioCheckbox; private @MonotonicNonNull CheckBox removeAudioCheckbox;
private @MonotonicNonNull CheckBox removeVideoCheckbox; private @MonotonicNonNull CheckBox removeVideoCheckbox;
@ -146,6 +156,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
private @MonotonicNonNull CheckBox enableHdrEditingCheckBox; private @MonotonicNonNull CheckBox enableHdrEditingCheckBox;
private @MonotonicNonNull Button selectDemoEffectsButton; private @MonotonicNonNull Button selectDemoEffectsButton;
private boolean @MonotonicNonNull [] demoEffectsSelections; private boolean @MonotonicNonNull [] demoEffectsSelections;
private @Nullable Uri localFileUri;
private int inputUriPosition; private int inputUriPosition;
private long trimStartMs; private long trimStartMs;
private long trimEndMs; private long trimEndMs;
@ -169,11 +180,10 @@ public final class ConfigurationActivity extends AppCompatActivity {
findViewById(R.id.transform_button).setOnClickListener(this::startTransformation); findViewById(R.id.transform_button).setOnClickListener(this::startTransformation);
selectFileButton = findViewById(R.id.select_file_button); flattenForSlowMotionCheckbox = findViewById(R.id.flatten_for_slow_motion_checkbox);
selectFileButton.setOnClickListener(this::selectFile);
selectedFileTextView = findViewById(R.id.selected_file_text_view); selectedFileTextView = findViewById(R.id.selected_file_text_view);
selectedFileTextView.setText(URI_DESCRIPTIONS[inputUriPosition]); selectedFileTextView.setText(PRESET_FILE_URI_DESCRIPTIONS[inputUriPosition]);
removeAudioCheckbox = findViewById(R.id.remove_audio_checkbox); removeAudioCheckbox = findViewById(R.id.remove_audio_checkbox);
removeAudioCheckbox.setOnClickListener(this::onRemoveAudio); removeAudioCheckbox.setOnClickListener(this::onRemoveAudio);
@ -181,7 +191,11 @@ public final class ConfigurationActivity extends AppCompatActivity {
removeVideoCheckbox = findViewById(R.id.remove_video_checkbox); removeVideoCheckbox = findViewById(R.id.remove_video_checkbox);
removeVideoCheckbox.setOnClickListener(this::onRemoveVideo); removeVideoCheckbox.setOnClickListener(this::onRemoveVideo);
flattenForSlowMotionCheckbox = findViewById(R.id.flatten_for_slow_motion_checkbox); selectPresetFileButton = findViewById(R.id.select_preset_file_button);
selectPresetFileButton.setOnClickListener(this::selectPresetFile);
selectLocalFileButton = findViewById(R.id.select_local_file_button);
selectLocalFileButton.setOnClickListener(this::selectLocalFile);
ArrayAdapter<String> audioMimeAdapter = ArrayAdapter<String> audioMimeAdapter =
new ArrayAdapter<>(/* context= */ this, R.layout.spinner_item); new ArrayAdapter<>(/* context= */ this, R.layout.spinner_item);
@ -239,6 +253,27 @@ public final class ConfigurationActivity extends AppCompatActivity {
demoEffectsSelections = new boolean[DEMO_EFFECTS.length]; demoEffectsSelections = new boolean[DEMO_EFFECTS.length];
selectDemoEffectsButton = findViewById(R.id.select_demo_effects_button); selectDemoEffectsButton = findViewById(R.id.select_demo_effects_button);
selectDemoEffectsButton.setOnClickListener(this::selectDemoEffects); selectDemoEffectsButton.setOnClickListener(this::selectDemoEffects);
localFilePickerLauncher =
registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
this::localFilePickerLauncherResult);
}
@Override
public void onRequestPermissionsResult(
int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == FILE_PERMISSION_REQUEST_CODE
&& grantResults.length == 1
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
launchLocalFilePicker();
} else {
Toast.makeText(
getApplicationContext(), getString(R.string.permission_denied), Toast.LENGTH_LONG)
.show();
}
} }
@Override @Override
@ -246,7 +281,8 @@ public final class ConfigurationActivity extends AppCompatActivity {
super.onResume(); super.onResume();
@Nullable Uri intentUri = getIntent().getData(); @Nullable Uri intentUri = getIntent().getData();
if (intentUri != null) { if (intentUri != null) {
checkNotNull(selectFileButton).setEnabled(false); checkNotNull(selectPresetFileButton).setEnabled(false);
checkNotNull(selectLocalFileButton).setEnabled(false);
checkNotNull(selectedFileTextView).setText(intentUri.toString()); checkNotNull(selectedFileTextView).setText(intentUri.toString());
} }
} }
@ -326,22 +362,64 @@ public final class ConfigurationActivity extends AppCompatActivity {
bundle.putFloat(PERIODIC_VIGNETTE_OUTER_RADIUS, periodicVignetteOuterRadius); bundle.putFloat(PERIODIC_VIGNETTE_OUTER_RADIUS, periodicVignetteOuterRadius);
transformerIntent.putExtras(bundle); transformerIntent.putExtras(bundle);
@Nullable Uri intentUri = getIntent().getData(); @Nullable Uri intentUri;
transformerIntent.setData( if (getIntent().getData() != null) {
intentUri != null ? intentUri : Uri.parse(INPUT_URIS[inputUriPosition])); intentUri = getIntent().getData();
} else if (localFileUri != null) {
intentUri = localFileUri;
} else {
intentUri = Uri.parse(PRESET_FILE_URIS[inputUriPosition]);
}
transformerIntent.setData(intentUri);
startActivity(transformerIntent); startActivity(transformerIntent);
} }
private void selectFile(View view) { private void selectPresetFile(View view) {
new AlertDialog.Builder(/* context= */ this) new AlertDialog.Builder(/* context= */ this)
.setTitle(R.string.select_file_title) .setTitle(R.string.select_preset_file_title)
.setSingleChoiceItems(URI_DESCRIPTIONS, inputUriPosition, this::selectFileInDialog) .setSingleChoiceItems(
PRESET_FILE_URI_DESCRIPTIONS, inputUriPosition, this::selectPresetFileInDialog)
.setPositiveButton(android.R.string.ok, /* listener= */ null) .setPositiveButton(android.R.string.ok, /* listener= */ null)
.create() .create()
.show(); .show();
} }
@RequiresNonNull("selectedFileTextView")
private void selectPresetFileInDialog(DialogInterface dialog, int which) {
inputUriPosition = which;
localFileUri = null;
selectedFileTextView.setText(PRESET_FILE_URI_DESCRIPTIONS[inputUriPosition]);
}
private void selectLocalFile(View view) {
int permissionStatus =
ActivityCompat.checkSelfPermission(
ConfigurationActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE);
if (permissionStatus != PackageManager.PERMISSION_GRANTED) {
String[] neededPermissions = {Manifest.permission.READ_EXTERNAL_STORAGE};
ActivityCompat.requestPermissions(
ConfigurationActivity.this, neededPermissions, FILE_PERMISSION_REQUEST_CODE);
} else {
launchLocalFilePicker();
}
}
private void launchLocalFilePicker() {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("video/*");
checkNotNull(localFilePickerLauncher).launch(intent);
}
@RequiresNonNull("selectedFileTextView")
private void localFilePickerLauncherResult(ActivityResult result) {
Intent data = result.getData();
if (data != null) {
localFileUri = checkNotNull(data.getData());
selectedFileTextView.setText(localFileUri.toString());
}
}
private void selectDemoEffects(View view) { private void selectDemoEffects(View view) {
new AlertDialog.Builder(/* context= */ this) new AlertDialog.Builder(/* context= */ this)
.setTitle(R.string.select_demo_effects) .setTitle(R.string.select_demo_effects)
@ -373,12 +451,6 @@ public final class ConfigurationActivity extends AppCompatActivity {
.show(); .show();
} }
@RequiresNonNull("selectedFileTextView")
private void selectFileInDialog(DialogInterface dialog, int which) {
inputUriPosition = which;
selectedFileTextView.setText(URI_DESCRIPTIONS[inputUriPosition]);
}
@RequiresNonNull("demoEffectsSelections") @RequiresNonNull("demoEffectsSelections")
private void selectDemoEffect(DialogInterface dialog, int which, boolean isChecked) { private void selectDemoEffect(DialogInterface dialog, int which, boolean isChecked) {
demoEffectsSelections[which] = isChecked; demoEffectsSelections[which] = isChecked;

View File

@ -34,16 +34,26 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<Button <Button
android:id="@+id/select_file_button" android:id="@+id/select_preset_file_button"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="32dp" android:layout_marginTop="32dp"
android:layout_marginStart="32dp" android:layout_marginStart="8dp"
android:layout_marginEnd="32dp" android:text="@string/select_preset_file_title"
android:text="@string/select_file_title" android:textSize="12sp"
app:layout_constraintTop_toBottomOf="@+id/configuration_text_view" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/configuration_text_view" />
<Button
android:id="@+id/select_local_file_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:layout_marginEnd="8dp"
android:text="@string/select_local_file_title"
android:textSize="12sp"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" /> app:layout_constraintTop_toBottomOf="@+id/configuration_text_view" />
<TextView <TextView
android:id="@+id/selected_file_text_view" android:id="@+id/selected_file_text_view"
android:layout_width="0dp" android:layout_width="0dp"
@ -57,7 +67,7 @@
android:gravity="center" android:gravity="center"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/select_file_button" /> app:layout_constraintTop_toBottomOf="@+id/select_preset_file_button" />
<androidx.core.widget.NestedScrollView <androidx.core.widget.NestedScrollView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"

View File

@ -17,7 +17,8 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" translatable="false">Transformer Demo</string> <string name="app_name" translatable="false">Transformer Demo</string>
<string name="configuration" translatable="false">Configuration</string> <string name="configuration" translatable="false">Configuration</string>
<string name="select_file_title" translatable="false">Choose file</string> <string name="select_preset_file_title" translatable="false">Choose preset file</string>
<string name="select_local_file_title">Choose local file</string>
<string name="remove_audio" translatable="false">Remove audio</string> <string name="remove_audio" translatable="false">Remove audio</string>
<string name="remove_video" translatable="false">Remove video</string> <string name="remove_video" translatable="false">Remove video</string>
<string name="flatten_for_slow_motion" translatable="false">Flatten for slow motion</string> <string name="flatten_for_slow_motion" translatable="false">Flatten for slow motion</string>
@ -61,6 +62,7 @@
<string name="lightness_adjustment">Lightness adjustment</string> <string name="lightness_adjustment">Lightness adjustment</string>
<string name="input_video">Input video:</string> <string name="input_video">Input video:</string>
<string name="output_video">Output video:</string> <string name="output_video">Output video:</string>
<string name="permission_denied">Permission Denied</string>
<string name="hide_input_video">Hide input video</string> <string name="hide_input_video">Hide input video</string>
<string name="show_input_video">Show input video</string> <string name="show_input_video">Show input video</string>
</resources> </resources>