| /* |
| * Copyright (C) 2013 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.timezonepicker; |
| |
| import android.content.Context; |
| import android.content.res.Resources; |
| import android.os.Build; |
| import android.text.Spannable; |
| import android.text.Spannable.Factory; |
| import android.text.format.DateUtils; |
| import android.text.format.Time; |
| import android.text.style.ForegroundColorSpan; |
| import android.util.Log; |
| |
| import java.util.Locale; |
| import java.util.TimeZone; |
| |
| public class TimeZonePickerUtils { |
| private static final String TAG = "TimeZonePickerUtils"; |
| |
| public static final int GMT_TEXT_COLOR = 0xFF888888; |
| public static final int DST_SYMBOL_COLOR = 0xFFBFBFBF; |
| private static final Factory mSpannableFactory = Spannable.Factory.getInstance(); |
| |
| private Locale mDefaultLocale; |
| private String[] mOverrideIds; |
| private String[] mOverrideLabels; |
| |
| /** |
| * This needs to be an instantiated class so that it doesn't need to continuously re-load the |
| * list of timezone IDs that need to be overridden. |
| * @param context |
| */ |
| public TimeZonePickerUtils(Context context) { |
| // Instead of saving a reference to the context (because we might need to look up the |
| // labels every time getGmtDisplayName is called), we'll cache the lists of override IDs |
| // and labels now. |
| cacheOverrides(context); |
| } |
| |
| /** |
| * Given a timezone id (e.g. America/Los_Angeles), returns the corresponding timezone |
| * display name (e.g. Pacific Time GMT-7). |
| * |
| * @param context Context in case the override labels need to be re-cached. |
| * @param id The timezone id |
| * @param millis The time (daylight savings or not) |
| * @param grayGmt Whether the "GMT+x" part of the returned string should be colored gray. |
| * @return The display name of the timezone. |
| */ |
| public CharSequence getGmtDisplayName(Context context, String id, long millis, |
| boolean grayGmt) { |
| TimeZone timezone = TimeZone.getTimeZone(id); |
| if (timezone == null) { |
| return null; |
| } |
| |
| final Locale defaultLocale = Locale.getDefault(); |
| if (!defaultLocale.equals(mDefaultLocale)) { |
| // If the IDs and labels haven't been set yet, or if the locale has been changed |
| // recently, we'll need to re-cache them. |
| mDefaultLocale = defaultLocale; |
| cacheOverrides(context); |
| } |
| return buildGmtDisplayName(timezone, millis, grayGmt); |
| } |
| |
| private CharSequence buildGmtDisplayName(TimeZone tz, long timeMillis, boolean grayGmt) { |
| Time time = new Time(tz.getID()); |
| time.set(timeMillis); |
| |
| StringBuilder sb = new StringBuilder(); |
| |
| String displayName = getDisplayName(tz, time.isDst != 0); |
| sb.append(displayName); |
| |
| sb.append(" "); |
| final int gmtOffset = tz.getOffset(timeMillis); |
| int gmtStart = sb.length(); |
| appendGmtOffset(sb, gmtOffset); |
| int gmtEnd = sb.length(); |
| |
| int symbolStart = 0; |
| int symbolEnd = 0; |
| if (tz.useDaylightTime()) { |
| sb.append(" "); |
| symbolStart = sb.length(); |
| sb.append(getDstSymbol()); // Sun symbol |
| symbolEnd = sb.length(); |
| } |
| |
| // Set the gray colors. |
| Spannable spannableText = mSpannableFactory.newSpannable(sb); |
| if (grayGmt) { |
| spannableText.setSpan(new ForegroundColorSpan(GMT_TEXT_COLOR), |
| gmtStart, gmtEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); |
| } |
| if (tz.useDaylightTime()) { |
| spannableText.setSpan(new ForegroundColorSpan(DST_SYMBOL_COLOR), |
| symbolStart, symbolEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); |
| } |
| |
| CharSequence gmtDisplayName = spannableText; |
| return gmtDisplayName; |
| } |
| |
| public static void appendGmtOffset(StringBuilder sb, final int gmtOffset) { |
| sb.append("GMT"); |
| |
| if (gmtOffset < 0) { |
| sb.append('-'); |
| } else { |
| sb.append('+'); |
| } |
| |
| final int p = Math.abs(gmtOffset); |
| sb.append(p / DateUtils.HOUR_IN_MILLIS); // Hour |
| |
| final int min = (p / (int) DateUtils.MINUTE_IN_MILLIS) % 60; |
| if (min != 0) { // Show minutes if non-zero |
| sb.append(':'); |
| if (min < 10) { |
| sb.append('0'); |
| } |
| sb.append(min); |
| } |
| } |
| |
| public static char getDstSymbol() { |
| if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { |
| return '\u2600'; // The Sun emoji icon. |
| } else { |
| return '*'; |
| } |
| } |
| |
| /** |
| * Gets the display name for the specified Timezone ID. If the ID matches the list of IDs that |
| * need to be have their default display names overriden, use the pre-set display name from |
| * R.arrays. |
| * @param id The timezone ID. |
| * @param daylightTime True for daylight time, false for standard time |
| * @return The display name of the timezone. This will just use the default display name, |
| * except that certain timezones have poor defaults, and should use the pre-set override labels |
| * from R.arrays. |
| */ |
| private String getDisplayName(TimeZone tz, boolean daylightTime) { |
| if (mOverrideIds == null || mOverrideLabels == null) { |
| // Just in case they somehow didn't get loaded correctly. |
| return tz.getDisplayName(daylightTime, TimeZone.LONG, Locale.getDefault()); |
| } |
| |
| for (int i = 0; i < mOverrideIds.length; i++) { |
| if (tz.getID().equals(mOverrideIds[i])) { |
| if (mOverrideLabels.length > i) { |
| return mOverrideLabels[i]; |
| } |
| Log.e(TAG, "timezone_rename_ids len=" + mOverrideIds.length + |
| " timezone_rename_labels len=" + mOverrideLabels.length); |
| break; |
| } |
| } |
| |
| // If the ID doesn't need to have the display name overridden, or if the labels were |
| // malformed, just use the default. |
| return tz.getDisplayName(daylightTime, TimeZone.LONG, Locale.getDefault()); |
| } |
| |
| private void cacheOverrides(Context context) { |
| Resources res = context.getResources(); |
| mOverrideIds = res.getStringArray(R.array.timezone_rename_ids); |
| mOverrideLabels = res.getStringArray(R.array.timezone_rename_labels); |
| } |
| } |