blob: 4a0d7e25a22b66a3c5b694c1b89a717bedadbc7f [file] [log] [blame]
Jason Monke5b770e2017-03-03 21:49:29 -05001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5 * except in compliance with the License. You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software distributed under the
10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11 * KIND, either express or implied. See the License for the specific language governing
12 * permissions and limitations under the License.
13 */
14
15package com.android.systemui.qs;
16
Charles Hece2a7c02017-10-11 20:25:20 +010017import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
Charles Hece2a7c02017-10-11 20:25:20 +010018
Rohan Shahd3cf7562018-02-23 11:12:28 -080019import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
Lucas Dupin1f7374a2018-02-26 18:08:33 -080021import android.app.ActivityManager;
Rohan Shahd3cf7562018-02-23 11:12:28 -080022import android.app.AlarmManager;
Jason Monke5b770e2017-03-03 21:49:29 -050023import android.content.Context;
Evan Laird4ea2a492018-01-22 11:29:12 -050024import android.content.Intent;
Jason Monke5b770e2017-03-03 21:49:29 -050025import android.content.res.Configuration;
Jason Monke5b770e2017-03-03 21:49:29 -050026import android.graphics.Color;
27import android.graphics.Rect;
Rohan Shahd3cf7562018-02-23 11:12:28 -080028import android.os.Handler;
Evan Laird4ea2a492018-01-22 11:29:12 -050029import android.provider.AlarmClock;
Jason Monke5b770e2017-03-03 21:49:29 -050030import android.support.annotation.VisibleForTesting;
Rohan Shahd3cf7562018-02-23 11:12:28 -080031import android.text.TextUtils;
32import android.text.format.DateUtils;
Jason Monke5b770e2017-03-03 21:49:29 -050033import android.util.AttributeSet;
Rohan Shaha160ab12018-03-09 15:47:36 -080034import android.util.Log;
Jason Monk824ffff2017-04-11 15:49:06 -040035import android.view.View;
Jason Monke5b770e2017-03-03 21:49:29 -050036import android.widget.RelativeLayout;
Rohan Shahd3cf7562018-02-23 11:12:28 -080037import android.widget.TextView;
Jason Monke5b770e2017-03-03 21:49:29 -050038
39import com.android.settingslib.Utils;
40import com.android.systemui.BatteryMeterView;
41import com.android.systemui.Dependency;
Rohan Shahd3cf7562018-02-23 11:12:28 -080042import com.android.systemui.Prefs;
Jason Monke5b770e2017-03-03 21:49:29 -050043import com.android.systemui.R;
Jason Monk01df36f2017-06-07 13:02:47 -040044import com.android.systemui.R.id;
Charles Hece2a7c02017-10-11 20:25:20 +010045import com.android.systemui.SysUiServiceProvider;
Jason Monke5b770e2017-03-03 21:49:29 -050046import com.android.systemui.plugins.ActivityStarter;
47import com.android.systemui.qs.QSDetail.Callback;
Charles Hece2a7c02017-10-11 20:25:20 +010048import com.android.systemui.statusbar.CommandQueue;
Evan Laird95896952018-01-22 19:30:05 -050049import com.android.systemui.statusbar.phone.StatusBarIconController;
50import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager;
Evan Laird39254d42018-01-18 16:05:30 -050051import com.android.systemui.statusbar.policy.DarkIconDispatcher;
Jason Monk824ffff2017-04-11 15:49:06 -040052import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
Rohan Shahd3cf7562018-02-23 11:12:28 -080053import com.android.systemui.statusbar.policy.NextAlarmController;
Jason Monke5b770e2017-03-03 21:49:29 -050054
Lucas Dupin1f7374a2018-02-26 18:08:33 -080055import java.util.Locale;
56
Rohan Shahd3cf7562018-02-23 11:12:28 -080057/**
58 * View that contains the top-most bits of the screen (primarily the status bar with date, time, and
59 * battery) and also contains the {@link QuickQSPanel} along with some of the panel's inner
60 * contents.
61 */
62public class QuickStatusBarHeader extends RelativeLayout implements CommandQueue.Callbacks,
63 View.OnClickListener, NextAlarmController.NextAlarmChangeCallback {
Rohan Shaha160ab12018-03-09 15:47:36 -080064 private static final String TAG = "QuickStatusBarHeader";
65 private static final boolean DEBUG = false;
Jason Monke5b770e2017-03-03 21:49:29 -050066
Rohan Shahd3cf7562018-02-23 11:12:28 -080067 /** Delay for auto fading out the long press tooltip after it's fully visible (in ms). */
68 private static final long AUTO_FADE_OUT_DELAY_MS = DateUtils.SECOND_IN_MILLIS * 6;
69 private static final int FADE_ANIMATION_DURATION_MS = 300;
70 private static final int TOOLTIP_NOT_YET_SHOWN_COUNT = 0;
Rohan Shahb6a915c2018-03-01 11:57:00 -080071 public static final int MAX_TOOLTIP_SHOWN_COUNT = 2;
Rohan Shahd3cf7562018-02-23 11:12:28 -080072
73 private final Handler mHandler = new Handler();
Jason Monke5b770e2017-03-03 21:49:29 -050074
75 private QSPanel mQsPanel;
76
77 private boolean mExpanded;
78 private boolean mListening;
Charles Hece2a7c02017-10-11 20:25:20 +010079 private boolean mQsDisabled;
Jason Monke5b770e2017-03-03 21:49:29 -050080
81 protected QuickQSPanel mHeaderQsPanel;
82 protected QSTileHost mHost;
Evan Laird95896952018-01-22 19:30:05 -050083 private TintedIconManager mIconManager;
Rohan Shahd3cf7562018-02-23 11:12:28 -080084 private TouchAnimator mStatusIconsAlphaAnimator;
85 private TouchAnimator mHeaderTextContainerAlphaAnimator;
Evan Laird95896952018-01-22 19:30:05 -050086
87 private View mQuickQsStatusIcons;
Evan Laird4ea2a492018-01-22 11:29:12 -050088 private View mDate;
Rohan Shahd3cf7562018-02-23 11:12:28 -080089 private View mHeaderTextContainerView;
90 /** View corresponding to the next alarm info (including the icon). */
91 private View mNextAlarmView;
92 /** Tooltip for educating users that they can long press on icons to see more details. */
93 private View mLongPressTooltipView;
94 /** {@link TextView} containing the actual text indicating when the next alarm will go off. */
95 private TextView mNextAlarmTextView;
96
97 private NextAlarmController mAlarmController;
98 private String mNextAlarmText;
99 /** Counts how many times the long press tooltip has been shown to the user. */
100 private int mShownCount;
101
102 /**
103 * Runnable for automatically fading out the long press tooltip (as if it were animating away).
104 */
105 private final Runnable mAutoFadeOutTooltipRunnable = () -> hideLongPressTooltip(false);
Evan Laird4ea2a492018-01-22 11:29:12 -0500106
Jason Monke5b770e2017-03-03 21:49:29 -0500107 public QuickStatusBarHeader(Context context, AttributeSet attrs) {
108 super(context, attrs);
Rohan Shahd3cf7562018-02-23 11:12:28 -0800109
110 mAlarmController = Dependency.get(NextAlarmController.class);
111 mShownCount = getStoredShownCount();
Jason Monke5b770e2017-03-03 21:49:29 -0500112 }
113
114 @Override
115 protected void onFinishInflate() {
116 super.onFinishInflate();
Jason Monke5b770e2017-03-03 21:49:29 -0500117
118 mHeaderQsPanel = findViewById(R.id.quick_qs_panel);
Evan Laird4ea2a492018-01-22 11:29:12 -0500119 mDate = findViewById(R.id.date);
120 mDate.setOnClickListener(this);
Evan Laird95896952018-01-22 19:30:05 -0500121 mQuickQsStatusIcons = findViewById(R.id.quick_qs_status_icons);
122 mIconManager = new TintedIconManager(findViewById(R.id.statusIcons));
Jason Monke5b770e2017-03-03 21:49:29 -0500123
Rohan Shahd3cf7562018-02-23 11:12:28 -0800124 // Views corresponding to the header info section (e.g. tooltip and next alarm).
125 mHeaderTextContainerView = findViewById(R.id.header_text_container);
126 mLongPressTooltipView = findViewById(R.id.long_press_tooltip);
127 mNextAlarmView = findViewById(R.id.next_alarm);
128 mNextAlarmTextView = findViewById(R.id.next_alarm_text);
Jason Monke5b770e2017-03-03 21:49:29 -0500129
130 updateResources();
131
Jason Monk824ffff2017-04-11 15:49:06 -0400132 Rect tintArea = new Rect(0, 0, 0, 0);
Evan Laird95896952018-01-22 19:30:05 -0500133 int colorForeground = Utils.getColorAttr(getContext(), android.R.attr.colorForeground);
134 float intensity = colorForeground == Color.WHITE ? 0 : 1;
135 int fillColor = fillColorForIntensity(intensity, getContext());
136
137 // Set light text on the header icons because they will always be on a black background
Evan Laird39254d42018-01-18 16:05:30 -0500138 applyDarkness(R.id.clock, tintArea, 0, DarkIconDispatcher.DEFAULT_ICON_TINT);
Evan Laird95896952018-01-22 19:30:05 -0500139 applyDarkness(id.signal_cluster, tintArea, intensity, colorForeground);
140
141 // Set the correct tint for the status icons so they contrast
142 mIconManager.setTint(fillColor);
Jason Monke5b770e2017-03-03 21:49:29 -0500143
144 BatteryMeterView battery = findViewById(R.id.battery);
145 battery.setForceShowPercent(true);
Jason Monke5b770e2017-03-03 21:49:29 -0500146 }
147
Jason Monk824ffff2017-04-11 15:49:06 -0400148 private void applyDarkness(int id, Rect tintArea, float intensity, int color) {
149 View v = findViewById(id);
150 if (v instanceof DarkReceiver) {
151 ((DarkReceiver) v).onDarkChanged(tintArea, intensity, color);
152 }
153 }
154
Evan Laird95896952018-01-22 19:30:05 -0500155 private int fillColorForIntensity(float intensity, Context context) {
156 if (intensity == 0) {
157 return context.getColor(R.color.light_mode_icon_color_dual_tone_fill);
158 }
159 return context.getColor(R.color.dark_mode_icon_color_dual_tone_fill);
160 }
161
Jason Monke5b770e2017-03-03 21:49:29 -0500162 @Override
163 protected void onConfigurationChanged(Configuration newConfig) {
164 super.onConfigurationChanged(newConfig);
165 updateResources();
166 }
167
168 @Override
169 public void onRtlPropertiesChanged(int layoutDirection) {
170 super.onRtlPropertiesChanged(layoutDirection);
171 updateResources();
172 }
173
174 private void updateResources() {
Rohan Shahd3cf7562018-02-23 11:12:28 -0800175 // Update height, especially due to landscape mode restricting space.
176 mHeaderTextContainerView.getLayoutParams().height =
177 mContext.getResources().getDimensionPixelSize(R.dimen.qs_header_tooltip_height);
178 mHeaderTextContainerView.setLayoutParams(mHeaderTextContainerView.getLayoutParams());
179
180 updateStatusIconAlphaAnimator();
181 updateHeaderTextContainerAlphaAnimator();
Evan Laird95896952018-01-22 19:30:05 -0500182 }
183
Rohan Shahd3cf7562018-02-23 11:12:28 -0800184 private void updateStatusIconAlphaAnimator() {
185 mStatusIconsAlphaAnimator = new TouchAnimator.Builder()
Evan Laird95896952018-01-22 19:30:05 -0500186 .addFloat(mQuickQsStatusIcons, "alpha", 1, 0)
187 .build();
Jason Monke5b770e2017-03-03 21:49:29 -0500188 }
189
Rohan Shahd3cf7562018-02-23 11:12:28 -0800190 private void updateHeaderTextContainerAlphaAnimator() {
191 mHeaderTextContainerAlphaAnimator = new TouchAnimator.Builder()
192 .addFloat(mHeaderTextContainerView, "alpha", 0, 1)
193 .setStartDelay(.5f)
194 .build();
Jason Monke5b770e2017-03-03 21:49:29 -0500195 }
196
197 public void setExpanded(boolean expanded) {
198 if (mExpanded == expanded) return;
199 mExpanded = expanded;
200 mHeaderQsPanel.setExpanded(expanded);
201 updateEverything();
202 }
203
Rohan Shahd3cf7562018-02-23 11:12:28 -0800204 /**
205 * Animates the inner contents based on the given expansion details.
206 *
207 * @param isKeyguardShowing whether or not we're showing the keyguard (a.k.a. lockscreen)
208 * @param expansionFraction how much the QS panel is expanded/pulled out (up to 1f)
209 * @param panelTranslationY how much the panel has physically moved down vertically (required
210 * for keyguard animations only)
211 */
212 public void setExpansion(boolean isKeyguardShowing, float expansionFraction,
213 float panelTranslationY) {
214 final float keyguardExpansionFraction = isKeyguardShowing ? 1f : expansionFraction;
215 if (mStatusIconsAlphaAnimator != null) {
216 mStatusIconsAlphaAnimator.setPosition(keyguardExpansionFraction);
Evan Laird95896952018-01-22 19:30:05 -0500217 }
Rohan Shahd3cf7562018-02-23 11:12:28 -0800218
219 if (isKeyguardShowing) {
220 // If the keyguard is showing, we want to offset the text so that it comes in at the
221 // same time as the panel as it slides down.
222 mHeaderTextContainerView.setTranslationY(panelTranslationY);
223 } else {
224 mHeaderTextContainerView.setTranslationY(0f);
225 }
226
227 if (mHeaderTextContainerAlphaAnimator != null) {
228 mHeaderTextContainerAlphaAnimator.setPosition(keyguardExpansionFraction);
229 }
230
231 // Check the original expansion fraction - we don't want to show the tooltip until the
232 // panel is pulled all the way out.
233 if (expansionFraction == 1f) {
234 // QS is fully expanded, bring in the tooltip.
235 showLongPressTooltip();
236 }
237 }
238
239 /** Returns the latest stored tooltip shown count from SharedPreferences. */
240 private int getStoredShownCount() {
241 return Prefs.getInt(
242 mContext,
243 Prefs.Key.QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT,
244 TOOLTIP_NOT_YET_SHOWN_COUNT);
Jason Monke5b770e2017-03-03 21:49:29 -0500245 }
246
247 @Override
Charles Hece2a7c02017-10-11 20:25:20 +0100248 public void disable(int state1, int state2, boolean animate) {
249 final boolean disabled = (state2 & DISABLE2_QUICK_SETTINGS) != 0;
250 if (disabled == mQsDisabled) return;
251 mQsDisabled = disabled;
252 mHeaderQsPanel.setDisabledByPolicy(disabled);
Evan Laird19bf52c2018-01-24 19:54:58 -0500253 final int rawHeight = (int) getResources().getDimension(
254 com.android.internal.R.dimen.quick_qs_total_height);
Charles Hece2a7c02017-10-11 20:25:20 +0100255 getLayoutParams().height = disabled ? (rawHeight - mHeaderQsPanel.getHeight()) : rawHeight;
256 }
257
258 @Override
259 public void onAttachedToWindow() {
260 SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).addCallbacks(this);
Evan Laird95896952018-01-22 19:30:05 -0500261 Dependency.get(StatusBarIconController.class).addIconGroup(mIconManager);
Charles Hece2a7c02017-10-11 20:25:20 +0100262 }
263
264 @Override
Jason Monke5b770e2017-03-03 21:49:29 -0500265 @VisibleForTesting
266 public void onDetachedFromWindow() {
267 setListening(false);
Charles Hece2a7c02017-10-11 20:25:20 +0100268 SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).removeCallbacks(this);
Evan Laird95896952018-01-22 19:30:05 -0500269 Dependency.get(StatusBarIconController.class).removeIconGroup(mIconManager);
Jason Monke5b770e2017-03-03 21:49:29 -0500270 super.onDetachedFromWindow();
271 }
272
273 public void setListening(boolean listening) {
274 if (listening == mListening) {
275 return;
276 }
277 mHeaderQsPanel.setListening(listening);
278 mListening = listening;
Rohan Shahd3cf7562018-02-23 11:12:28 -0800279
280 if (listening) {
281 mAlarmController.addCallback(this);
282 } else {
283 mAlarmController.removeCallback(this);
284 }
Jason Monke5b770e2017-03-03 21:49:29 -0500285 }
286
Evan Laird95896952018-01-22 19:30:05 -0500287 @Override
288 public void onClick(View v) {
289 if(v == mDate){
290 Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(new Intent(
291 AlarmClock.ACTION_SHOW_ALARMS),0);
292 }
293 }
294
Rohan Shahd3cf7562018-02-23 11:12:28 -0800295 @Override
296 public void onNextAlarmChanged(AlarmManager.AlarmClockInfo nextAlarm) {
Lucas Dupin1f7374a2018-02-26 18:08:33 -0800297 mNextAlarmText = nextAlarm != null ? formatNextAlarm(nextAlarm) : null;
Rohan Shaha160ab12018-03-09 15:47:36 -0800298
Rohan Shahd3cf7562018-02-23 11:12:28 -0800299 if (mNextAlarmText != null) {
300 hideLongPressTooltip(true /* shouldFadeInAlarmText */);
301 } else {
302 hideAlarmText();
303 }
304 updateHeaderTextContainerAlphaAnimator();
305 }
306
307 /**
308 * Animates in the long press tooltip (as long as the next alarm text isn't currently occupying
309 * the space).
310 */
311 public void showLongPressTooltip() {
312 // If we have alarm text to show, don't bother fading in the tooltip.
313 if (!TextUtils.isEmpty(mNextAlarmText)) {
314 return;
315 }
316
317 if (mShownCount < MAX_TOOLTIP_SHOWN_COUNT) {
318 mLongPressTooltipView.animate().cancel();
319 mLongPressTooltipView.setVisibility(View.VISIBLE);
320 mLongPressTooltipView.animate()
321 .alpha(1f)
322 .setDuration(FADE_ANIMATION_DURATION_MS)
323 .setListener(new AnimatorListenerAdapter() {
324 @Override
325 public void onAnimationEnd(Animator animation) {
326 mHandler.postDelayed(
327 mAutoFadeOutTooltipRunnable, AUTO_FADE_OUT_DELAY_MS);
328 }
329 })
330 .start();
331
332 // Increment and drop the shown count in prefs for the next time we're deciding to
333 // fade in the tooltip. We first sanity check that the tooltip count hasn't changed yet
334 // in prefs (say, from a long press).
335 if (getStoredShownCount() <= mShownCount) {
336 Prefs.putInt(mContext, Prefs.Key.QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT, ++mShownCount);
337 }
338 }
339 }
340
341 /**
342 * Fades out the long press tooltip if it's partially visible - short circuits any running
343 * animation. Additionally has the ability to fade in the alarm info text.
344 *
345 * @param shouldShowAlarmText whether we should fade in the next alarm text
346 */
347 private void hideLongPressTooltip(boolean shouldShowAlarmText) {
348 mLongPressTooltipView.animate().cancel();
349 if (mLongPressTooltipView.getVisibility() == View.VISIBLE
350 && mLongPressTooltipView.getAlpha() != 0f) {
351 mHandler.removeCallbacks(mAutoFadeOutTooltipRunnable);
352 mLongPressTooltipView.animate()
353 .alpha(0f)
354 .setDuration(FADE_ANIMATION_DURATION_MS)
355 .setListener(new AnimatorListenerAdapter() {
356 @Override
357 public void onAnimationEnd(Animator animation) {
Rohan Shaha160ab12018-03-09 15:47:36 -0800358 if (DEBUG) Log.d(TAG, "hideLongPressTooltip: Hid long press tip");
Rohan Shahd3cf7562018-02-23 11:12:28 -0800359 mLongPressTooltipView.setVisibility(View.INVISIBLE);
360
361 if (shouldShowAlarmText) {
362 showAlarmText();
363 }
364 }
365 })
366 .start();
367 } else {
368 mLongPressTooltipView.setVisibility(View.INVISIBLE);
Rohan Shahd3cf7562018-02-23 11:12:28 -0800369 if (shouldShowAlarmText) {
370 showAlarmText();
371 }
372 }
373 }
374
375 /**
376 * Fades in the updated alarm text. Note that if there's already an alarm showing, this will
377 * immediately hide it and fade in the updated time.
378 */
379 private void showAlarmText() {
380 mNextAlarmView.setAlpha(0f);
381 mNextAlarmView.setVisibility(View.VISIBLE);
382 mNextAlarmTextView.setText(mNextAlarmText);
383
Rohan Shaha160ab12018-03-09 15:47:36 -0800384 // Animate the alarm back in. Make sure to clear the animator listener for the animation!
Rohan Shahd3cf7562018-02-23 11:12:28 -0800385 mNextAlarmView.animate()
386 .alpha(1f)
387 .setDuration(FADE_ANIMATION_DURATION_MS)
Rohan Shaha160ab12018-03-09 15:47:36 -0800388 .setListener(null)
Rohan Shahd3cf7562018-02-23 11:12:28 -0800389 .start();
390 }
391
392 /**
393 * Fades out and hides the next alarm text. This also resets the text contents to null in
394 * preparation for the next alarm update.
395 */
396 private void hideAlarmText() {
397 if (mNextAlarmView.getVisibility() == View.VISIBLE) {
398 mNextAlarmView.animate()
399 .alpha(0f)
400 .setListener(new AnimatorListenerAdapter() {
401 @Override
402 public void onAnimationEnd(Animator animation) {
Rohan Shaha160ab12018-03-09 15:47:36 -0800403 if (DEBUG) Log.d(TAG, "hideAlarmText: Hid alarm text");
404
Rohan Shahd3cf7562018-02-23 11:12:28 -0800405 // Reset the alpha regardless of how the animation ends for the next
406 // time we show this view/want to animate it.
407 mNextAlarmView.setVisibility(View.INVISIBLE);
408 mNextAlarmView.setAlpha(1f);
409 mNextAlarmTextView.setText(null);
410 }
411 })
412 .start();
413 } else {
414 // Next alarm view is already hidden, only need to clear the text.
415 mNextAlarmTextView.setText(null);
416 }
417 }
418
Jason Monke5b770e2017-03-03 21:49:29 -0500419 public void updateEverything() {
Jason Monk1fdde2d2017-03-08 09:39:21 -0500420 post(() -> setClickable(false));
Jason Monke5b770e2017-03-03 21:49:29 -0500421 }
422
423 public void setQSPanel(final QSPanel qsPanel) {
424 mQsPanel = qsPanel;
425 setupHost(qsPanel.getHost());
426 }
427
428 public void setupHost(final QSTileHost host) {
429 mHost = host;
430 //host.setHeaderView(mExpandIndicator);
431 mHeaderQsPanel.setQSPanelAndHeader(mQsPanel, this);
432 mHeaderQsPanel.setHost(host, null /* No customization in header */);
Evan Lairdef160f22018-01-29 14:08:45 -0500433
434 // Use SystemUI context to get battery meter colors, and let it use the default tint (white)
435 BatteryMeterView battery = findViewById(R.id.battery);
436 battery.setColorsFromContext(mHost.getContext());
437 battery.onDarkChanged(new Rect(), 0, DarkIconDispatcher.DEFAULT_ICON_TINT);
Jason Monke5b770e2017-03-03 21:49:29 -0500438 }
439
440 public void setCallback(Callback qsPanelCallback) {
441 mHeaderQsPanel.setCallback(qsPanelCallback);
442 }
Lucas Dupin1f7374a2018-02-26 18:08:33 -0800443
444 private String formatNextAlarm(AlarmManager.AlarmClockInfo info) {
445 if (info == null) {
446 return "";
447 }
448 String skeleton = android.text.format.DateFormat
449 .is24HourFormat(mContext, ActivityManager.getCurrentUser()) ? "EHm" : "Ehma";
450 String pattern = android.text.format.DateFormat
451 .getBestDateTimePattern(Locale.getDefault(), skeleton);
452 return android.text.format.DateFormat.format(pattern, info.getTriggerTime()).toString();
453 }
Jason Monke5b770e2017-03-03 21:49:29 -0500454}