blob: 83ef4f940ad14ab353ad8e3204cfa21272b7ac3e [file] [log] [blame]
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settingslib.utils;
import android.content.Context;
import android.icu.text.MeasureFormat;
import android.icu.text.MeasureFormat.FormatWidth;
import android.icu.text.RelativeDateTimeFormatter;
import android.icu.text.RelativeDateTimeFormatter.RelativeUnit;
import android.icu.util.Measure;
import android.icu.util.MeasureUnit;
import android.icu.util.ULocale;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.style.TtsSpan;
import com.android.settingslib.R;
import java.util.ArrayList;
import java.util.Locale;
/** Utility class for generally useful string methods **/
public class StringUtil {
public static final int SECONDS_PER_MINUTE = 60;
public static final int SECONDS_PER_HOUR = 60 * 60;
public static final int SECONDS_PER_DAY = 24 * 60 * 60;
/**
* Returns elapsed time for the given millis, in the following format:
* 2 days, 5 hr, 40 min, 29 sec
*
* @param context the application context
* @param millis the elapsed time in milli seconds
* @param withSeconds include seconds?
* @return the formatted elapsed time
*/
public static CharSequence formatElapsedTime(Context context, double millis,
boolean withSeconds) {
SpannableStringBuilder sb = new SpannableStringBuilder();
int seconds = (int) Math.floor(millis / 1000);
if (!withSeconds) {
// Round up.
seconds += 30;
}
int days = 0, hours = 0, minutes = 0;
if (seconds >= SECONDS_PER_DAY) {
days = seconds / SECONDS_PER_DAY;
seconds -= days * SECONDS_PER_DAY;
}
if (seconds >= SECONDS_PER_HOUR) {
hours = seconds / SECONDS_PER_HOUR;
seconds -= hours * SECONDS_PER_HOUR;
}
if (seconds >= SECONDS_PER_MINUTE) {
minutes = seconds / SECONDS_PER_MINUTE;
seconds -= minutes * SECONDS_PER_MINUTE;
}
final ArrayList<Measure> measureList = new ArrayList(4);
if (days > 0) {
measureList.add(new Measure(days, MeasureUnit.DAY));
}
if (hours > 0) {
measureList.add(new Measure(hours, MeasureUnit.HOUR));
}
if (minutes > 0) {
measureList.add(new Measure(minutes, MeasureUnit.MINUTE));
}
if (withSeconds && seconds > 0) {
measureList.add(new Measure(seconds, MeasureUnit.SECOND));
}
if (measureList.size() == 0) {
// Everything addable was zero, so nothing was added. We add a zero.
measureList.add(new Measure(0, withSeconds ? MeasureUnit.SECOND : MeasureUnit.MINUTE));
}
final Measure[] measureArray = measureList.toArray(new Measure[measureList.size()]);
final Locale locale = context.getResources().getConfiguration().locale;
final MeasureFormat measureFormat = MeasureFormat.getInstance(
locale, FormatWidth.SHORT);
sb.append(measureFormat.formatMeasures(measureArray));
if (measureArray.length == 1 && MeasureUnit.MINUTE.equals(measureArray[0].getUnit())) {
// Add ttsSpan if it only have minute value, because it will be read as "meters"
final TtsSpan ttsSpan = new TtsSpan.MeasureBuilder().setNumber(minutes)
.setUnit("minute").build();
sb.setSpan(ttsSpan, 0, sb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
return sb;
}
/**
* Returns relative time for the given millis in the past with different format style.
* In a short format such as "2 days ago", "5 hr. ago", "40 min. ago", or "29 sec. ago".
* In a long format such as "2 days ago", "5 hours ago", "40 minutes ago" or "29 seconds ago".
*
* <p>The unit is chosen to have good information value while only using one unit. So 27 hours
* and 50 minutes would be formatted as "28 hr. ago", while 50 hours would be formatted as
* "2 days ago".
*
* @param context the application context
* @param millis the elapsed time in milli seconds
* @param withSeconds include seconds?
* @param formatStyle format style
* @return the formatted elapsed time
*/
public static CharSequence formatRelativeTime(Context context, double millis,
boolean withSeconds, RelativeDateTimeFormatter.Style formatStyle) {
final int seconds = (int) Math.floor(millis / 1000);
final RelativeUnit unit;
final int value;
if (withSeconds && seconds < 2 * SECONDS_PER_MINUTE) {
return context.getResources().getString(R.string.time_unit_just_now);
} else if (seconds < 2 * SECONDS_PER_HOUR) {
unit = RelativeUnit.MINUTES;
value = (seconds + SECONDS_PER_MINUTE / 2)
/ SECONDS_PER_MINUTE;
} else if (seconds < 2 * SECONDS_PER_DAY) {
unit = RelativeUnit.HOURS;
value = (seconds + SECONDS_PER_HOUR / 2)
/ SECONDS_PER_HOUR;
} else {
unit = RelativeUnit.DAYS;
value = (seconds + SECONDS_PER_DAY / 2)
/ SECONDS_PER_DAY;
}
final Locale locale = context.getResources().getConfiguration().locale;
final RelativeDateTimeFormatter formatter = RelativeDateTimeFormatter.getInstance(
ULocale.forLocale(locale),
null /* default NumberFormat */,
formatStyle,
android.icu.text.DisplayContext.CAPITALIZATION_FOR_MIDDLE_OF_SENTENCE);
return formatter.format(value, RelativeDateTimeFormatter.Direction.LAST, unit);
}
/**
* Returns relative time for the given millis in the past, in a long format such as "2 days
* ago", "5 hours ago", "40 minutes ago" or "29 seconds ago".
*
* <p>The unit is chosen to have good information value while only using one unit. So 27 hours
* and 50 minutes would be formatted as "28 hr. ago", while 50 hours would be formatted as
* "2 days ago".
*
* @param context the application context
* @param millis the elapsed time in milli seconds
* @param withSeconds include seconds?
* @return the formatted elapsed time
* @deprecated use {@link #formatRelativeTime(Context, double, boolean,
* RelativeDateTimeFormatter.Style)} instead.
*/
@Deprecated
public static CharSequence formatRelativeTime(Context context, double millis,
boolean withSeconds) {
return formatRelativeTime(context, millis, withSeconds,
RelativeDateTimeFormatter.Style.LONG);
}
}