Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 400a110a authored by Matías Hernández's avatar Matías Hernández Committed by Android (Google) Code Review
Browse files

Merge "MetricStyle: Use Chronometer for paused durations, too" into main

parents 27d2723c 5685e1ee
Loading
Loading
Loading
Loading
+11 −75
Original line number Diff line number Diff line
@@ -17,10 +17,10 @@
package android.app;
import static android.annotation.Dimension.DP;
import static android.app.Flags.FLAG_HIDE_STATUS_BAR_NOTIFICATION;
import static android.app.Flags.FLAG_NM_SUMMARIZATION;
import static android.app.Flags.FLAG_NM_SUMMARIZATION_ALL;
import static android.app.Flags.FLAG_NOTIFICATION_IS_ANIMATED_ACTION_API;
import static android.app.Flags.FLAG_HIDE_STATUS_BAR_NOTIFICATION;
import static android.app.Flags.apiMetricStyle;
import static android.app.Flags.notificationsRedesignTemplates;
import static android.app.admin.DevicePolicyResources.Drawables.Source.NOTIFICATION;
@@ -35,7 +35,6 @@ import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static com.android.internal.util.Preconditions.checkArgument;
import static java.time.temporal.ChronoUnit.SECONDS;
import static java.util.Objects.requireNonNull;
import android.annotation.ColorInt;
@@ -82,9 +81,6 @@ import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.icu.number.NumberFormatter;
import android.icu.number.Precision;
import android.icu.text.MeasureFormat;
import android.icu.util.Measure;
import android.icu.util.MeasureUnit;
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.PlayerBase;
@@ -11853,8 +11849,7 @@ public class Notification implements Parcelable
                        contentView.setTextViewText(metricView.unitId(), valueString.subtext());
                    }
                    if (metricValue instanceof Metric.TimeDifference timeDifference
                            && timeDifference.getPausedDuration() == null) {
                    if (metricValue instanceof Metric.TimeDifference timeDifference) {
                        contentView.setViewVisibility(metricView.textValueId(), View.GONE);
                        contentView.setViewVisibility(metricView.chronometerId(), View.VISIBLE);
                        contentView.setChronometerCountDown(
@@ -11868,9 +11863,13 @@ public class Notification implements Parcelable
                            contentView.setChronometer(metricView.chronometerId(),
                                    timeDifference.getZeroElapsedRealtime(), /* format= */ null,
                                    /* started= */ true);
                        } else if (timeDifference.getPausedDuration() != null) {
                            contentView.setChronometerPaused(metricView.chronometerId(),
                                    timeDifference.getPausedDuration());
                        } else {
                            throw new IllegalStateException(
                                    "No zeroTime for running TimeDifference in " + metric);
                                    "No zeroTime or pausedDuration for running TimeDifference in "
                                            + metric);
                        }
                        // TODO(b/434910979): implement format support for Chronometer.
                    } else {
@@ -12057,6 +12056,8 @@ public class Notification implements Parcelable
                public ValueString(String text) {
                    this(text, null);
                }
                private static final ValueString EMPTY = new ValueString("", null);
            }
            /**
@@ -12357,73 +12358,8 @@ public class Notification implements Parcelable
            @NonNull
            @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
            public ValueString toValueString(Context context) {
                Duration duration = getCurrentDuration();
                duration = duration.truncatedTo(SECONDS); // ms are ignored and we don't want -0:00
                Duration absDuration = duration.abs();
                Measure hours = new Measure(absDuration.toHours(), MeasureUnit.HOUR);
                Measure minutes = new Measure(absDuration.toMinutesPart(), MeasureUnit.MINUTE);
                Measure seconds = new Measure(absDuration.toSecondsPart(), MeasureUnit.SECOND);
                String absText = formatAbsoluteDuration(mFormat, hours, minutes, seconds);
                String text = duration.isNegative()
                        ? context.getString(R.string.negative_duration, absText)
                        : absText;
                return new ValueString(text, null);
            }
            private Duration getCurrentDuration() {
                if (mPausedDuration != null) {
                    return mPausedDuration;
                } else if (mZeroTime != null) {
                    // If the timer/stopwatch is running we likely want a Chronometer view, so this
                    // path is mostly for debugging/completeness.
                    Instant now = getSystemClock().instant();
                    if (isStopwatch()) {
                        return Duration.between(mZeroTime, now);
                    } else {
                        return Duration.between(now, mZeroTime);
                    }
                } else if (mZeroElapsedRealtime != null) {
                    // If the timer/stopwatch is running we likely want a Chronometer view, so this
                    // path is mostly for debugging/completeness.
                    long elapsedRealtimeNow = getElapsedRealtimeClock().getAsLong();
                    if (isStopwatch()) {
                        return Duration.ofMillis(elapsedRealtimeNow - mZeroElapsedRealtime);
                    } else {
                        return Duration.ofMillis(mZeroElapsedRealtime - elapsedRealtimeNow);
                    }
                } else {
                    throw new IllegalStateException(
                            "None of mPausedDuration, mZeroTime, mZeroElapsedRealtime set!");
                }
            }
            private static String formatAbsoluteDuration(@Format int format, Measure hours,
                    Measure minutes, Measure seconds) {
                if (format == FORMAT_ADAPTIVE) {
                    MeasureFormat formatter = MeasureFormat.getInstance(Locale.getDefault(),
                            MeasureFormat.FormatWidth.NARROW);
                    ArrayList<Measure> partsList = new ArrayList<>();
                    if (hours.getNumber().intValue() != 0) {
                        partsList.add(hours);
                    }
                    if (minutes.getNumber().intValue() != 0) {
                        partsList.add(minutes);
                    }
                    if (seconds.getNumber().intValue() != 0 || partsList.isEmpty()) {
                        partsList.add(seconds);
                    }
                    return formatter.formatMeasures(partsList.toArray(new Measure[0]));
                } else {
                    // FORMAT_AUTOMATIC / FORMAT_CHRONOMETER
                    MeasureFormat formatter = MeasureFormat.getInstance(Locale.getDefault(),
                            MeasureFormat.FormatWidth.NUMERIC);
                    return hours.getNumber().intValue() != 0
                            ? formatter.formatMeasures(hours, minutes, seconds)
                            : formatter.formatMeasures(minutes, seconds);
                }
                // Not used; Chronometer view will take charge of formatting.
                return ValueString.EMPTY;
            }
        }
+17 −0
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@ import android.widget.RemoteViews.RemoteView;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;

import java.time.Duration;
import java.time.Instant;
import java.time.InstantSource;
import java.util.ArrayList;
@@ -224,6 +225,22 @@ public class Chronometer extends TextView {
                + (instant.toEpochMilli() - mSystemClock.millis());
    }

    /**
     * Pauses the Chronometer (if it was running) and displays the specified {@link Duration}
     * (which can be negative). To do this, {@link #getBase()} will be modified according to the
     * current value of {@link #isCountDown()}.
     *
     * @hide
     */
    @android.view.RemotableViewMethod
    public void setPausedDuration(@NonNull Duration duration) {
        stop();
        long elapsedRealtime = mElapsedRealtimeClock.getAsLong();
        mBase = elapsedRealtime + (isCountDown() ? 1 : -1) * duration.toMillis();
        mBaseInstant = null;
        updateText(elapsedRealtime);
    }

    /**
     * Return the base time as set through {@link #setBase}.
     */
+81 −0
Original line number Diff line number Diff line
@@ -147,6 +147,7 @@ import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayDeque;
import java.util.ArrayList;
@@ -2172,6 +2173,8 @@ public class RemoteViews implements Parcelable, Filter {
                return BlendMode.class;
            case BaseReflectionAction.INSTANT:
                return Instant.class;
            case BaseReflectionAction.DURATION:
                return Duration.class;
            default:
                return null;
        }
@@ -2737,6 +2740,7 @@ public class RemoteViews implements Parcelable, Filter {
        static final int ICON = 16;
        static final int BLEND_MODE = 17;
        static final int INSTANT = 18;
        static final int DURATION = 19;

        @UnsupportedAppUsage
        String mMethodName;
@@ -2971,6 +2975,13 @@ public class RemoteViews implements Parcelable, Filter {
                        mValue = null;
                    }
                    break;
                case DURATION:
                    if (in.readInt() == 1) {
                        mValue = Duration.ofSeconds(in.readLong(), in.readInt());
                    } else {
                        mValue = null;
                    }
                    break;
                default:
                    break;
            }
@@ -3033,6 +3044,15 @@ public class RemoteViews implements Parcelable, Filter {
                        out.writeInt(0);
                    }
                    break;
                case DURATION:
                    if (mValue != null) {
                        out.writeInt(1);
                        out.writeLong(((Duration) this.mValue).getSeconds());
                        out.writeInt(((Duration) this.mValue).getNano());
                    } else {
                        out.writeInt(0);
                    }
                    break;
                default:
                    break;
            }
@@ -3131,6 +3151,11 @@ public class RemoteViews implements Parcelable, Filter {
                    case INSTANT:
                        writeInstantToProto(out, (Instant) this.mValue,
                                RemoteViewsProto.ReflectionAction.INSTANT_VALUE);
                        break;
                    case DURATION:
                        writeDurationToProto(out, (Duration) this.mValue,
                                RemoteViewsProto.ReflectionAction.DURATION_VALUE);
                        break;
                    case BUNDLE:
                    case INTENT:
                    default:
@@ -3229,6 +3254,12 @@ public class RemoteViews implements Parcelable, Filter {
                        values.put(RemoteViewsProto.ReflectionAction.INSTANT_VALUE,
                                createInstantFromProto(in,
                                        RemoteViewsProto.ReflectionAction.INSTANT_VALUE));
                        break;
                    case (int) RemoteViewsProto.ReflectionAction.DURATION_VALUE:
                        values.put(RemoteViewsProto.ReflectionAction.DURATION_VALUE,
                                createDurationFromProto(in,
                                        RemoteViewsProto.ReflectionAction.DURATION_VALUE));
                        break;
                    default:
                        Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
                                + ProtoUtils.currentFieldToString(in));
@@ -3309,6 +3340,11 @@ public class RemoteViews implements Parcelable, Filter {
                    case INSTANT:
                        value = (Instant) values.get(
                                RemoteViewsProto.ReflectionAction.INSTANT_VALUE);
                        break;
                    case DURATION:
                        value = (Duration) values.get(
                                RemoteViewsProto.ReflectionAction.DURATION_VALUE);
                        break;
                    case BUNDLE:
                    case INTENT:
                    default:
@@ -6954,6 +6990,22 @@ public class RemoteViews implements Parcelable, Filter {
        setBoolean(viewId, "setStarted", started);
    }

    /**
     * Equivalent to calling {@link Chronometer#setPausedDuration(Duration)} (which will set the
     * chronometer to paused and the base so that the displayed time is {@code pausedDuration}).
     *
     * <p>{@link #setChronometerCountDown(int, boolean)} should be called <em>before</em> this
     * method, so that the base time can be computed correctly.
     *
     * @param viewId The id of the {@link Chronometer} to change
     * @param pausedDuration the time that the {@link Chronometer} should display
     *
     * @hide
     */
    public void setChronometerPaused(@IdRes int viewId, Duration pausedDuration) {
        setDuration(viewId, "setPausedDuration", pausedDuration);
    }

    /**
     * Equivalent to calling {@link Chronometer#setCountDown(boolean) Chronometer.setCountDown} on
     * the chronometer with the given viewId.
@@ -7957,6 +8009,19 @@ public class RemoteViews implements Parcelable, Filter {
        addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.INSTANT, value));
    }

    /**
     * Call a method taking one {@link Duration} on a view in the layout for this RemoteViews.
     *
     * @param viewId The id of the view on which to call the method.
     * @param methodName The name of the method to call.
     * @param value The value to pass to the method.
     *
     * @hide
     */
    public void setDuration(@IdRes int viewId, String methodName, Duration value) {
        addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.DURATION, value));
    }

    /**
     * Call a method taking one Bundle on a view in the layout for this RemoteViews.
     *
@@ -10757,4 +10822,20 @@ public class RemoteViews implements Parcelable, Filter {
        in.end(token);
        return instant;
    }

    private static void writeDurationToProto(ProtoOutputStream out, Duration duration,
            long fieldId) {
        long token = out.start(fieldId);
        RemoteViewsSerializers.writeDurationToProto(out, duration);
        out.end(token);
    }

    private static Duration createDurationFromProto(ProtoInputStream in, long fieldId)
            throws Exception {
        long token = in.start(fieldId);
        Duration duration = RemoteViewsSerializers.createDurationFromProto(in);
        in.end(token);
        return duration;
    }

}
+30 −0
Original line number Diff line number Diff line
@@ -77,6 +77,7 @@ import androidx.annotation.NonNull;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
@@ -260,6 +261,35 @@ public class RemoteViewsSerializers {
        return Instant.ofEpochSecond(seconds, nanos);
    }

    /** Write {@link Duration} to proto. */
    public static void writeDurationToProto(@NonNull ProtoOutputStream out,
            @NonNull Duration duration) {
        out.write(RemoteViewsProto.Duration.SECONDS, duration.getSeconds());
        out.write(RemoteViewsProto.Duration.NANOS, duration.getNano());
    }

    /** Create {@link Duration} from proto. */
    public static Duration createDurationFromProto(@NonNull ProtoInputStream in)
            throws IOException {
        long seconds = 0;
        int nanos = 0;
        while (in.nextField() != NO_MORE_FIELDS) {
            switch (in.getFieldNumber()) {
                case (int) RemoteViewsProto.Duration.SECONDS:
                    seconds = in.readLong(RemoteViewsProto.Duration.SECONDS);
                    break;
                case (int) RemoteViewsProto.Duration.NANOS:
                    nanos = in.readInt(RemoteViewsProto.Duration.NANOS);
                    break;
                default:
                    Log.w(TAG, "Unhandled field while reading Duration proto!\n"
                            + ProtoUtils.currentFieldToString(in));
            }
        }

        return Duration.ofSeconds(seconds, nanos);
    }

    public static void writeCharSequenceToProto(@NonNull ProtoOutputStream out,
            @NonNull CharSequence cs) {
        out.write(RemoteViewsProto.CharSequence.TEXT, cs.toString());
+17 −0
Original line number Diff line number Diff line
@@ -108,6 +108,22 @@ message RemoteViewsProto {
      optional int32 nanos = 2;
    }

    /** A java.util.time.Duration. */
    message Duration {
      // Signed seconds of the span of time. Must be from -315,576,000,000
      // to +315,576,000,000 inclusive. Note: these bounds are computed from:
      // 60 sec/min * 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years
      optional int64 seconds = 1;

      // Signed fractions of a second at nanosecond resolution of the span
      // of time. Durations less than one second are represented with a 0
      // `seconds` field and a positive or negative `nanos` field. For durations
      // of one second or more, a non-zero value for the `nanos` field must be
      // of the same sign as the `seconds` field. Must be from -999,999,999
      // to +999,999,999 inclusive.
      optional int32 nanos = 2;
    }

    /**
     * Represents a CharSequence with Spans.
     */
@@ -401,6 +417,7 @@ message RemoteViewsProto {
            Icon icon_value = 17;
            int32 blend_mode_value = 18;
            Instant instant_value = 19;
            Duration duration_value = 20;
            // Intent and Bundle values are excluded.
        }
    }
Loading