diff --git a/app/src/main/java/protect/videotranscoder/activity/MainActivity.java b/app/src/main/java/protect/videotranscoder/activity/MainActivity.java index 707db3eb..1a51b421 100644 --- a/app/src/main/java/protect/videotranscoder/activity/MainActivity.java +++ b/app/src/main/java/protect/videotranscoder/activity/MainActivity.java @@ -31,6 +31,7 @@ import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.EditText; +import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.Spinner; import android.widget.TextView; @@ -75,6 +76,7 @@ public class MainActivity extends AppCompatActivity public static final String MESSENGER_INTENT_KEY = BuildConfig.APPLICATION_ID + ".MESSENGER_INTENT_KEY"; public static final String FFMPEG_ENCODE_ARGS = BuildConfig.APPLICATION_ID + ".FFMPEG_ENCODE_ARGS"; public static final String FFMPEG_OUTPUT_FILE = BuildConfig.APPLICATION_ID + ".FFMPEG_OUTPUT_FILE"; + public static final String FFMPEG_FAILURE_MSG = BuildConfig.APPLICATION_ID + ".FFMPEG_FAILURE_MSG"; public static final String OUTPUT_MIMETYPE = BuildConfig.APPLICATION_ID + ".OUTPUT_MIMETYPE"; public static final String OUTPUT_DURATION_MS = BuildConfig.APPLICATION_ID + ".OUTPUT_DURATION_MS"; @@ -128,6 +130,10 @@ public class MainActivity extends AppCompatActivity private Button selectVideoButton; private Button encodeButton; private Button cancelButton; + private ImageView startJumpBack; + private ImageView startJumpForward; + private ImageView endJumpBack; + private ImageView endJumpForward; private MediaInfo videoInfo; private JobScheduler schedulerService; @@ -145,6 +151,11 @@ protected void onCreate(Bundle savedInstanceState) encodeButton = findViewById(R.id.encode); cancelButton = findViewById(R.id.cancel); + startJumpBack = findViewById(R.id.startJumpBack); + startJumpForward = findViewById(R.id.startJumpForward); + endJumpBack = findViewById(R.id.endJumpBack); + endJumpForward = findViewById(R.id.endJumpForward); + tvLeft = findViewById(R.id.tvLeft); tvRight = findViewById(R.id.tvRight); @@ -520,10 +531,28 @@ private void updateUiForEncoding() selectVideoButton.setVisibility(View.GONE); encodeButton.setVisibility(View.GONE); + startJumpBack.setVisibility(View.GONE); + startJumpForward.setVisibility(View.GONE); + endJumpBack.setVisibility(View.GONE); + endJumpForward.setVisibility(View.GONE); + cancelButton.setVisibility(View.VISIBLE); progressBar.setVisibility(View.VISIBLE); } + private void updateUiForVideoSettings() + { + selectVideoButton.setVisibility(View.VISIBLE); + encodeButton.setVisibility(View.VISIBLE); + startJumpBack.setVisibility(View.VISIBLE); + startJumpForward.setVisibility(View.VISIBLE); + endJumpBack.setVisibility(View.VISIBLE); + endJumpForward.setVisibility(View.VISIBLE); + + cancelButton.setVisibility(View.GONE); + progressBar.setVisibility(View.GONE); + } + private void cancelEncode() { FFmpegUtil.cancelCall(); @@ -536,6 +565,8 @@ private void cancelEncode() } scheduler.cancelAll(); + + updateUiForVideoSettings(); } private boolean isEncoding() @@ -553,15 +584,21 @@ private void stopVideoPlayback() } } - private void startVideoPlayback() + private void startVideoPlayback(Integer positionSec) { stopVideoPlayback(); int startTimeSec = rangeSeekBar.getSelectedMinValue().intValue(); int stopTimeSec = rangeSeekBar.getSelectedMaxValue().intValue(); - int durationSec = stopTimeSec - startTimeSec; - videoView.seekTo(startTimeSec * 1000); + if(positionSec == null) + { + positionSec = startTimeSec; + } + + int durationSec = stopTimeSec - positionSec; + + videoView.seekTo(positionSec * 1000); videoView.start(); videoTimer = new Timer(); @@ -570,7 +607,7 @@ private void startVideoPlayback() @Override public void run() { - startVideoPlayback(); + startVideoPlayback(null); } }, durationSec * 1000); } @@ -600,7 +637,7 @@ protected void onResume() if(isEncoding() == false) { - startVideoPlayback(); + startVideoPlayback(null); } } @@ -656,6 +693,10 @@ private void populateOptionDefaults() } encodeButton.setVisibility(View.VISIBLE); + startJumpBack.setVisibility(View.VISIBLE); + startJumpForward.setVisibility(View.VISIBLE); + endJumpBack.setVisibility(View.VISIBLE); + endJumpForward.setVisibility(View.VISIBLE); containerSpinner.setAdapter(new ArrayAdapter<>(this, R.layout.spinner_textview, MediaContainer.values())); containerSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() @@ -866,7 +907,7 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) @Override public void onPrepared(MediaPlayer mp) { - int durationMs = mp.getDuration(); + final int durationMs = mp.getDuration(); tvLeft.setVisibility(View.VISIBLE); tvLeft.setText(getTime(0)); tvRight.setVisibility(View.VISIBLE); @@ -880,18 +921,75 @@ public void onPrepared(MediaPlayer mp) rangeSeekBar.setOnRangeSeekbarChangeListener(new OnRangeSeekbarChangeListener() { + Number prevMinValueSec = 0; + Number prevMaxValueSec = (int)(durationMs / 1000f); + + // If the end time slider was moved, resume the playback + // this may seconds before the end + static final int END_PLAYBACK_HEADROOM_SEC = 3; + @Override public void valueChanged(Number minValue, Number maxValue) { + Integer playStartSec = null; + + if(prevMaxValueSec.intValue() != maxValue.intValue()) + { + // End time was changed + prevMaxValueSec = maxValue; + playStartSec = prevMaxValueSec.intValue(); + + // Resume playback a few seconds before the end + int headroom = Math.min(maxValue.intValue()-minValue.intValue(), END_PLAYBACK_HEADROOM_SEC); + playStartSec -= headroom; + } + + if(prevMinValueSec.intValue() != minValue.intValue()) + { + // Start time was changed + prevMinValueSec = minValue; + playStartSec = prevMinValueSec.intValue(); + } + tvLeft.setText(getTime(minValue.intValue())); tvRight.setText(getTime(maxValue.intValue())); - if(isEncoding() == false) + if(isEncoding() == false && playStartSec != null) { - startVideoPlayback(); + startVideoPlayback(playStartSec); } } }); + + class RangeSeekChanger implements View.OnClickListener + { + private final int startOffset; + private final int endOffset; + RangeSeekChanger(int startOffset, int endOffset) + { + this.startOffset = startOffset; + this.endOffset = endOffset; + } + + @Override + public void onClick(View v) + { + rangeSeekBar.setMinValue(0); + rangeSeekBar.setMaxValue(durationMs / 1000f); + + int selectedStart = rangeSeekBar.getSelectedMinValue().intValue(); + int selectedEnd = rangeSeekBar.getSelectedMaxValue().intValue(); + + rangeSeekBar.setMinStartValue(selectedStart + startOffset); + rangeSeekBar.setMaxStartValue(selectedEnd + endOffset); + rangeSeekBar.apply(); + } + } + + startJumpForward.setOnClickListener(new RangeSeekChanger(1, 0)); + startJumpBack.setOnClickListener(new RangeSeekChanger(-1, 0)); + endJumpForward.setOnClickListener(new RangeSeekChanger(0, 1)); + endJumpBack.setOnClickListener(new RangeSeekChanger(0, -1)); } }); @@ -992,6 +1090,7 @@ public void handleMessage(Message msg) boolean result = false; String outputFile = null; String mimetype = null; + String message = null; if(messageId == MessageId.JOB_SUCCEDED_MSG) { @@ -999,9 +1098,13 @@ public void handleMessage(Message msg) outputFile = ((Bundle)msg.obj).getString(FFMPEG_OUTPUT_FILE); mimetype = ((Bundle)msg.obj).getString(OUTPUT_MIMETYPE); } + else + { + message = ((Bundle)msg.obj).getString(FFMPEG_FAILURE_MSG); + } Log.d(TAG, "Job complete, result: " + result); - showEncodeCompleteDialog(mainActivity, result, outputFile, mimetype); + showEncodeCompleteDialog(mainActivity, result, message, outputFile, mimetype); break; case FFMPEG_UNSUPPORTED_MSG: @@ -1015,13 +1118,9 @@ public void handleMessage(Message msg) } private void showEncodeCompleteDialog(final MainActivity mainActivity, final boolean result, - final String outputFile, final String mimetype) + final String ffmpegMessage, final String outputFile, + final String mimetype) { - ProgressBar progressBar = mainActivity.findViewById(R.id.encodeProgress); - Button selectVideoButton = mainActivity.findViewById(R.id.selectVideo); - Button encodeButton = mainActivity.findViewById(R.id.encode); - Button cancelButton = mainActivity.findViewById(R.id.cancel); - Log.d(TAG, "Encode result: " + result); String message; @@ -1032,12 +1131,20 @@ private void showEncodeCompleteDialog(final MainActivity mainActivity, final boo } else { - message = mainActivity.getResources().getString(R.string.transcodeFailed); + message = mainActivity.getResources().getString(R.string.transcodeFailed, ffmpegMessage); } AlertDialog.Builder builder = new AlertDialog.Builder(mainActivity) .setMessage(message) .setCancelable(true) + .setOnDismissListener(new DialogInterface.OnDismissListener() + { + @Override + public void onDismiss(DialogInterface dialog) + { + mainActivity.startVideoPlayback(null); + } + }) .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) @@ -1070,12 +1177,7 @@ public void onClick(DialogInterface dialog, int which) builder.show(); - //startVideoPlayback(); - - selectVideoButton.setVisibility(View.VISIBLE); - encodeButton.setVisibility(View.VISIBLE); - cancelButton.setVisibility(View.GONE); - progressBar.setVisibility(View.GONE); + mainActivity.updateUiForVideoSettings(); } } diff --git a/app/src/main/java/protect/videotranscoder/service/FFmpegProcessService.java b/app/src/main/java/protect/videotranscoder/service/FFmpegProcessService.java index e2052147..7bcae049 100644 --- a/app/src/main/java/protect/videotranscoder/service/FFmpegProcessService.java +++ b/app/src/main/java/protect/videotranscoder/service/FFmpegProcessService.java @@ -13,9 +13,11 @@ import com.github.hiteshsondhi88.libffmpeg.ExecuteBinaryResponseHandler; import protect.videotranscoder.FFmpegUtil; +import protect.videotranscoder.R; import protect.videotranscoder.ResultCallbackHandler; import static protect.videotranscoder.activity.MainActivity.FFMPEG_ENCODE_ARGS; +import static protect.videotranscoder.activity.MainActivity.FFMPEG_FAILURE_MSG; import static protect.videotranscoder.activity.MainActivity.FFMPEG_OUTPUT_FILE; import static protect.videotranscoder.activity.MainActivity.MESSENGER_INTENT_KEY; import static protect.videotranscoder.activity.MainActivity.OUTPUT_DURATION_MS; @@ -77,7 +79,13 @@ public void onFailure(String s) Log.d(TAG, "Failed with output : " + s); jobFinished(params, false); - sendMessage(MessageId.JOB_FAILED_MSG, params.getJobId()); + // The last line of the output should be the failure message + String [] lines = s.split("\n"); + String failureMg = lines[lines.length-1].trim(); + + Bundle bundle = new Bundle(); + bundle.putString(FFMPEG_FAILURE_MSG, failureMg); + sendMessage(MessageId.JOB_FAILED_MSG, bundle); } @Override @@ -142,7 +150,9 @@ public boolean onStopJob(JobParameters params) FFmpegUtil.cancelCall(); - sendMessage(MessageId.JOB_FAILED_MSG, params.getJobId()); + Bundle bundle = new Bundle(); + bundle.putString(FFMPEG_FAILURE_MSG, getResources().getString(R.string.encodeCanceled)); + sendMessage(MessageId.JOB_FAILED_MSG, bundle); // Return false to drop the job. return false; diff --git a/app/src/main/res/drawable-hdpi/ic_fast_forward_black_24dp.png b/app/src/main/res/drawable-hdpi/ic_fast_forward_black_24dp.png new file mode 100644 index 00000000..3cd2adea Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_fast_forward_black_24dp.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_fast_rewind_black_24dp.png b/app/src/main/res/drawable-hdpi/ic_fast_rewind_black_24dp.png new file mode 100644 index 00000000..c5a902db Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_fast_rewind_black_24dp.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_fast_forward_black_24dp.png b/app/src/main/res/drawable-mdpi/ic_fast_forward_black_24dp.png new file mode 100644 index 00000000..70a4bbcf Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_fast_forward_black_24dp.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_fast_rewind_black_24dp.png b/app/src/main/res/drawable-mdpi/ic_fast_rewind_black_24dp.png new file mode 100644 index 00000000..7dc7471e Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_fast_rewind_black_24dp.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_fast_forward_black_24dp.png b/app/src/main/res/drawable-xhdpi/ic_fast_forward_black_24dp.png new file mode 100644 index 00000000..6f26faa6 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_fast_forward_black_24dp.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_fast_rewind_black_24dp.png b/app/src/main/res/drawable-xhdpi/ic_fast_rewind_black_24dp.png new file mode 100644 index 00000000..78c67476 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_fast_rewind_black_24dp.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_fast_forward_black_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_fast_forward_black_24dp.png new file mode 100644 index 00000000..abd54533 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_fast_forward_black_24dp.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_fast_rewind_black_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_fast_rewind_black_24dp.png new file mode 100644 index 00000000..72c70c45 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_fast_rewind_black_24dp.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_fast_forward_black_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_fast_forward_black_24dp.png new file mode 100644 index 00000000..35017177 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_fast_forward_black_24dp.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_fast_rewind_black_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_fast_rewind_black_24dp.png new file mode 100644 index 00000000..8839147e Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_fast_rewind_black_24dp.png differ diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 60180dd2..f004fa5e 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -54,19 +54,35 @@ android:id="@+id/buttonContainer" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_alignParentTop="true" android:layout_centerHorizontal="true" + android:layout_below="@id/tvRight" android:orientation="horizontal"> + +