Skip to content

Commit

Permalink
WIP: add dialog to adjust the synchronization of text captions
Browse files Browse the repository at this point in the history
status:
=======

* works great
  - is not yet feature complete

* this as-not-yet-modified version of 'MultiFieldTimePickerDialog'
  - does not:
    * support negative values
    * implement NumberPicker.OnValueChangeListener
      - for any change, immediately call:
          notifyDateSet() => mListener.onTimeSet(hr, min, sec, ms)
      - would allow the user to see the updates in realtime
        and tweak the values interactively alongside video playback

based on:
=========

https://github.com/chromium/chromium/raw/80.0.3987.137/content/public/android/java/src/org/chromium/content/browser/picker/MultiFieldTimePickerDialog.java
https://github.com/chromium/chromium/raw/80.0.3987.137/content/public/android/java/src/org/chromium/content/browser/picker/InputDialogContainer.java
https://github.com/chromium/chromium/raw/80.0.3987.137/content/public/android/java/res/layout/multi_field_time_picker_dialog.xml
  • Loading branch information
warren-bank committed Mar 13, 2020
1 parent cdb3fcb commit c82ca47
Show file tree
Hide file tree
Showing 11 changed files with 606 additions and 12 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,289 @@
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package com.github.warren_bank.exoplayer_airplay_receiver.ui.exoplayer2;

import com.github.warren_bank.exoplayer_airplay_receiver.R;

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.NumberPicker;

import java.util.ArrayList;
import java.util.Locale;

/**
* A time picker dialog with upto 5 number pickers left to right:
* hour, minute, second, milli, AM/PM.
*
* If is24hourFormat is true then AM/PM picker is not displayed and
* hour range is 0..23. Otherwise hour range is 1..12.
* The milli picker is not displayed if step >= SECOND_IN_MILLIS
* The second picker is not displayed if step >= MINUTE_IN_MILLIS.
*/
public class MultiFieldTimePickerDialog
extends AlertDialog implements OnClickListener {

private final NumberPicker mHourSpinner;
private final NumberPicker mMinuteSpinner;
private final NumberPicker mSecSpinner;
private final NumberPicker mMilliSpinner;
private final NumberPicker mAmPmSpinner;
private final OnMultiFieldTimeSetListener mListener;
private final int mStep;
private final int mBaseMilli;
private final boolean mIs24hourFormat;

/**
* Adds an onTimeSet() method.
*/
public interface OnMultiFieldTimeSetListener {
void onTimeSet(int hourOfDay, int minute, int second, int milli);
}

private static final int SECOND_IN_MILLIS = 1000;
private static final int MINUTE_IN_MILLIS = 60 * SECOND_IN_MILLIS;
private static final int HOUR_IN_MILLIS = 60 * MINUTE_IN_MILLIS;

public MultiFieldTimePickerDialog(
Context context,
int theme,
int hour, int minute, int second, int milli,
int min, int max, int step, boolean is24hourFormat,
OnMultiFieldTimeSetListener listener) {
super(context, theme);
mListener = listener;
mStep = step;
mIs24hourFormat = is24hourFormat;

if (min >= max) {
min = 0;
max = 24 * HOUR_IN_MILLIS - 1;
}
if (step < 0 || step >= 24 * HOUR_IN_MILLIS) {
step = MINUTE_IN_MILLIS;
}

LayoutInflater inflater =
(LayoutInflater) context.getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.multi_field_time_picker_dialog, null);
setView(view);

mHourSpinner = (NumberPicker) view.findViewById(R.id.hour);
mMinuteSpinner = (NumberPicker) view.findViewById(R.id.minute);
mSecSpinner = (NumberPicker) view.findViewById(R.id.second);
mMilliSpinner = (NumberPicker) view.findViewById(R.id.milli);
mAmPmSpinner = (NumberPicker) view.findViewById(R.id.ampm);

int minHour = min / HOUR_IN_MILLIS;
int maxHour = max / HOUR_IN_MILLIS;
min -= minHour * HOUR_IN_MILLIS;
max -= maxHour * HOUR_IN_MILLIS;

if (minHour == maxHour) {
mHourSpinner.setEnabled(false);
hour = minHour;
}

if (is24hourFormat) {
mAmPmSpinner.setVisibility(View.GONE);
} else {
int minAmPm = minHour / 12;
int maxAmPm = maxHour / 12;
int amPm = hour / 12;
mAmPmSpinner.setMinValue(minAmPm);
mAmPmSpinner.setMaxValue(maxAmPm);
mAmPmSpinner.setDisplayedValues(new String[] {
context.getString(R.string.time_picker_dialog_am),
context.getString(R.string.time_picker_dialog_pm)
});

hour %= 12;
if (hour == 0) {
hour = 12;
}
if (minAmPm == maxAmPm) {
mAmPmSpinner.setEnabled(false);
amPm = minAmPm;

minHour %= 12;
maxHour %= 12;
if (minHour == 0 && maxHour == 0) {
minHour = 12;
maxHour = 12;
} else if (minHour == 0) {
minHour = maxHour;
maxHour = 12;
} else if (maxHour == 0) {
maxHour = 12;
}
} else {
minHour = 1;
maxHour = 12;
}
mAmPmSpinner.setValue(amPm);
}

if (minHour == maxHour) {
mHourSpinner.setEnabled(false);
}
mHourSpinner.setMinValue(minHour);
mHourSpinner.setMaxValue(maxHour);
mHourSpinner.setValue(hour);

NumberFormatter twoDigitPaddingFormatter = new NumberFormatter("%02d");

int minMinute = min / MINUTE_IN_MILLIS;
int maxMinute = max / MINUTE_IN_MILLIS;
min -= minMinute * MINUTE_IN_MILLIS;
max -= maxMinute * MINUTE_IN_MILLIS;

if (minHour == maxHour) {
mMinuteSpinner.setMinValue(minMinute);
mMinuteSpinner.setMaxValue(maxMinute);
if (minMinute == maxMinute) {
// Set this otherwise the box is empty until you stroke it.
mMinuteSpinner.setDisplayedValues(
new String[] { twoDigitPaddingFormatter.format(minMinute) });
mMinuteSpinner.setEnabled(false);
minute = minMinute;
}
} else {
mMinuteSpinner.setMinValue(0);
mMinuteSpinner.setMaxValue(59);
}

mMinuteSpinner.setValue(minute);
if (step % HOUR_IN_MILLIS == 0) {
mMinuteSpinner.setEnabled(false);
// TODO(tkent): We should set minutes value of
// WebDateTimeChooserParams::stepBase.
mMinuteSpinner.setValue(minMinute);
}

mMinuteSpinner.setFormatter(twoDigitPaddingFormatter);

if (step >= MINUTE_IN_MILLIS) {
// Remove the ':' in front of the second spinner as well.
view.findViewById(R.id.second_colon).setVisibility(View.GONE);
mSecSpinner.setVisibility(View.GONE);
}

int minSecond = min / SECOND_IN_MILLIS;
int maxSecond = max / SECOND_IN_MILLIS;
min -= minSecond * SECOND_IN_MILLIS;
max -= maxSecond * SECOND_IN_MILLIS;

if (minHour == maxHour && minMinute == maxMinute) {
mSecSpinner.setMinValue(minSecond);
mSecSpinner.setMaxValue(maxSecond);
if (minSecond == maxSecond) {
// Set this otherwise the box is empty until you stroke it.
mSecSpinner.setDisplayedValues(
new String[] { twoDigitPaddingFormatter.format(minSecond) });
mSecSpinner.setEnabled(false);
second = minSecond;
}
} else {
mSecSpinner.setMinValue(0);
mSecSpinner.setMaxValue(59);
}

mSecSpinner.setValue(second);
mSecSpinner.setFormatter(twoDigitPaddingFormatter);

if (step >= SECOND_IN_MILLIS) {
// Remove the '.' in front of the milli spinner as well.
view.findViewById(R.id.second_dot).setVisibility(View.GONE);
mMilliSpinner.setVisibility(View.GONE);
}

// Round to the nearest step.
milli = ((milli + step / 2) / step) * step;
if (step == 1 || step == 10 || step == 100) {
if (minHour == maxHour && minMinute == maxMinute && minSecond == maxSecond) {
mMilliSpinner.setMinValue(min / step);
mMilliSpinner.setMaxValue(max / step);

if (min == max) {
mMilliSpinner.setEnabled(false);
milli = min;
}
} else {
mMilliSpinner.setMinValue(0);
mMilliSpinner.setMaxValue(999 / step);
}

if (step == 1) {
mMilliSpinner.setFormatter(new NumberFormatter("%03d"));
} else if (step == 10) {
mMilliSpinner.setFormatter(new NumberFormatter("%02d"));
} else if (step == 100) {
mMilliSpinner.setFormatter(new NumberFormatter("%d"));
}
mMilliSpinner.setValue(milli / step);
mBaseMilli = 0;
} else if (step < SECOND_IN_MILLIS) {
// Non-decimal step value.
ArrayList<String> strValue = new ArrayList<String>();
for (int i = min; i < max; i += step) {
strValue.add(String.format(Locale.getDefault(), "%03d", i));
}
mMilliSpinner.setMinValue(0);
mMilliSpinner.setMaxValue(strValue.size() - 1);
mMilliSpinner.setValue((milli - min) / step);
mMilliSpinner.setDisplayedValues(strValue.toArray(new String[strValue.size()]));
mBaseMilli = min;
} else {
mBaseMilli = 0;
}
}

@Override
public void onClick(DialogInterface dialog, int which) {
notifyDateSet();
}

private void notifyDateSet() {
int hour = getPickerValue(mHourSpinner);
int minute = getPickerValue(mMinuteSpinner);
int sec = getPickerValue(mSecSpinner);
int milli = getPickerValue(mMilliSpinner) * mStep + mBaseMilli;
if (!mIs24hourFormat) {
int ampm = getPickerValue(mAmPmSpinner);
if (hour == 12) {
hour = 0;
}
hour += ampm * 12;
}
mListener.onTimeSet(hour, minute, sec, milli);
}

/**
* Clear focus before retrieving so that values inserted with
* keyboard are taken into account.
*/
private int getPickerValue(NumberPicker picker) {
picker.clearFocus();
return picker.getValue();
}

private static class NumberFormatter implements NumberPicker.Formatter {
private final String mFormat;

NumberFormatter(String format) {
mFormat = format;
}

@Override
public String format(int value) {
return String.format(Locale.getDefault(), mFormat, value);
}
}
}
Loading

0 comments on commit c82ca47

Please sign in to comment.