Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qtimezoneprivate_android.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// Copyright (C) 2014 Drew Parsons <dparsons@emerall.com>
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4// Qt-Security score:significant reason:default
5
6#include "qtimezone.h"
8
9#include <QtCore/QJniEnvironment>
10#include <QtCore/QSet>
11#include <QtCore/qjnitypes.h>
12
14
15Q_DECLARE_JNI_CLASS(TimeZone, "java/util/TimeZone");
16Q_DECLARE_JNI_CLASS(Locale, "java/util/Locale");
17Q_DECLARE_JNI_CLASS(Date, "java/util/Date");
18
19/*
20 Private
21
22 Android implementation
23
24 Note that a QJniObject manages a global reference, so it serves as an
25 owning smart-pointer, ensuring an object doesn't get garbage-collected
26 before we're done with it.
27*/
28
29// Create the system default time zone
30QAndroidTimeZonePrivate::QAndroidTimeZonePrivate()
31 : QTimeZonePrivate()
32{
33 // Keep in sync with systemTimeZoneId():
34 androidTimeZone = QJniObject::callStaticMethod<QtJniTypes::TimeZone>(
35 QtJniTypes::Traits<QtJniTypes::TimeZone>::className(), "getDefault");
36 const QJniObject id = androidTimeZone.callMethod<jstring>("getID");
37 m_id = id.toString().toUtf8();
38}
39
40// Create a named time zone
41QAndroidTimeZonePrivate::QAndroidTimeZonePrivate(const QByteArray &ianaId)
42 : QTimeZonePrivate()
43{
44 init(ianaId);
45}
46
47QAndroidTimeZonePrivate::~QAndroidTimeZonePrivate()
48{
49}
50
51static QString getDisplayName(QJniObject zone, jint style, jboolean dst,
52 const QLocale &locale)
53{
54 QJniObject jbcpTag = QJniObject::fromString(locale.bcp47Name());
55 QJniObject jlocale = QJniObject::callStaticMethod<QtJniTypes::Locale>(
56 QtJniTypes::Traits<QtJniTypes::Locale>::className(), "forLanguageTag",
57 jbcpTag.object<jstring>());
58
59 return zone.callMethod<jstring>("getDisplayName", dst, style,
60 jlocale.object<QtJniTypes::Locale>()).toString();
61}
62
63void QAndroidTimeZonePrivate::init(const QByteArray &ianaId)
64{
65 const QString iana = QString::fromUtf8(ianaId);
66 androidTimeZone = QJniObject::callStaticMethod<QtJniTypes::TimeZone>(
67 QtJniTypes::Traits<QtJniTypes::TimeZone>::className(), "getTimeZone",
68 QJniObject::fromString(iana).object<jstring>());
69
70 // The ID or display name of the zone we've got, if it looks like what we asked for:
71 const auto match = [iana](const QString &name) -> QByteArray {
72 if (iana.compare(name, Qt::CaseInsensitive) == 0)
73 return name.toUtf8();
74
75 return QByteArray();
76 };
77
78 // Painfully, JNI gives us back a default zone object if it doesn't
79 // recognize the name; so check for whether ianaId is a recognized name of
80 // the zone object we got and ignore the zone if not.
81 // Try checking ianaId against getID(), getDisplayName():
82 m_id = match(androidTimeZone.callMethod<jstring>("getID").toString());
83 for (int style = 1; m_id.isEmpty() && style >= 0; --style) {
84 for (int dst = 1; m_id.isEmpty() && dst >= 0; --dst) {
85 for (int pick = 2; m_id.isEmpty() && pick >= 0; --pick) {
86 QLocale locale = (pick == 0 ? QLocale::system()
87 : pick == 1 ? QLocale() : QLocale::c());
88 m_id = match(getDisplayName(androidTimeZone, style, jboolean(dst), locale));
89 }
90 }
91 }
92}
93
94QAndroidTimeZonePrivate *QAndroidTimeZonePrivate::clone() const
95{
96 return new QAndroidTimeZonePrivate(*this);
97}
98
99
100QString QAndroidTimeZonePrivate::displayName(QTimeZone::TimeType timeType, QTimeZone::NameType nameType,
101 const QLocale &locale) const
102{
103 QString name;
104
105 if (androidTimeZone.isValid()) {
106 jboolean daylightTime = (timeType == QTimeZone::DaylightTime); // treat QTimeZone::GenericTime as QTimeZone::StandardTime
107
108 // treat all NameTypes as java TimeZone style LONG (value 1), except of course QTimeZone::ShortName which is style SHORT (value 0);
109 jint style = (nameType == QTimeZone::ShortName ? 0 : 1);
110
111 name = getDisplayName(androidTimeZone, style, daylightTime, locale);
112 }
113
114 return name;
115}
116
117QString QAndroidTimeZonePrivate::abbreviation(qint64 atMSecsSinceEpoch) const
118{
119 if ( isDaylightTime( atMSecsSinceEpoch ) )
120 return displayName(QTimeZone::DaylightTime, QTimeZone::ShortName, QLocale() );
121 else
122 return displayName(QTimeZone::StandardTime, QTimeZone::ShortName, QLocale() );
123}
124
125int QAndroidTimeZonePrivate::offsetFromUtc(qint64 atMSecsSinceEpoch) const
126{
127 // offsetFromUtc( ) is invoked when androidTimeZone may not yet be set at startup,
128 // so a validity test is needed here
129 if ( androidTimeZone.isValid() ) {
130 // the java method getOffset() returns milliseconds, but QTimeZone returns seconds
131 return androidTimeZone.callMethod<jint>(
132 "getOffset", static_cast<jlong>(atMSecsSinceEpoch) ) / 1000;
133 } else {
134 return 0;
135 }
136}
137
138int QAndroidTimeZonePrivate::standardTimeOffset(qint64 atMSecsSinceEpoch) const
139{
140 // the java method does not use a reference time
141 Q_UNUSED( atMSecsSinceEpoch );
142 if ( androidTimeZone.isValid() )
143 // the java method getRawOffset() returns milliseconds, but QTimeZone returns seconds
144 return androidTimeZone.callMethod<jint>( "getRawOffset" ) / 1000;
145 else
146 return 0;
147}
148
149int QAndroidTimeZonePrivate::daylightTimeOffset(qint64 atMSecsSinceEpoch) const
150{
151 return ( offsetFromUtc(atMSecsSinceEpoch) - standardTimeOffset(atMSecsSinceEpoch) );
152}
153
154bool QAndroidTimeZonePrivate::hasDaylightTime() const
155{
156 if ( androidTimeZone.isValid() )
157 /* note: the Java function only tests for future DST transitions, not past */
158 return androidTimeZone.callMethod<jboolean>("useDaylightTime" );
159 else
160 return false;
161}
162
163bool QAndroidTimeZonePrivate::isDaylightTime(qint64 atMSecsSinceEpoch) const
164{
165 if ( androidTimeZone.isValid() ) {
166 QJniObject jDate = QJniObject::construct<QtJniTypes::Date>(
167 static_cast<jlong>(atMSecsSinceEpoch));
168 return androidTimeZone.callMethod<jboolean>("inDaylightTime",
169 jDate.object<QtJniTypes::Date>());
170 }
171 else
172 return false;
173}
174
175QTimeZonePrivate::Data QAndroidTimeZonePrivate::data(qint64 forMSecsSinceEpoch) const
176{
177 if (androidTimeZone.isValid()) {
178 return Data(abbreviation(forMSecsSinceEpoch), forMSecsSinceEpoch,
179 offsetFromUtc(forMSecsSinceEpoch),
180 standardTimeOffset(forMSecsSinceEpoch));
181 }
182 return {};
183}
184
185// java.util.TimeZone does not directly provide transitions,
186// so don't over-ride QTZP's base implementation of transition methods.
187
188QByteArray QAndroidTimeZonePrivate::systemTimeZoneId() const
189{
190 // Keep in sync with default constructor:
191 QJniObject androidSystemTimeZone = QJniObject::callStaticMethod<QtJniTypes::TimeZone>(
192 QtJniTypes::Traits<QtJniTypes::TimeZone>::className(), "getDefault");
193 const QJniObject id = androidSystemTimeZone.callMethod<jstring>("getID");
194 return id.toString().toUtf8();
195}
196
197bool QAndroidTimeZonePrivate::isTimeZoneIdAvailable(const QByteArray &ianaId) const
198{
199 QAndroidTimeZonePrivate probe(ianaId);
200 return probe.isValid();
201}
202
203QList<QByteArray> QAndroidTimeZonePrivate::availableTimeZoneIds() const
204{
205 using namespace QtJniTypes;
206
207 const QJniArray androidAvailableIdList
208 = TimeZone::callStaticMethod<String[]>("getAvailableIDs");
209 // Does not document order of entries.
210
211 QList<QByteArray> result;
212 result.reserve(androidAvailableIdList.size());
213 for (const auto &id : androidAvailableIdList)
214 result.append(id.toString().toUtf8());
215
216 // Sort & uniquify (just to be sure; it appears to not need this, but we can't rely on that).
217 std::sort(result.begin(), result.end());
218 result.erase(std::unique(result.begin(), result.end()), result.end());
219 return result;
220}
221
222QT_END_NAMESPACE
Combined button and popup list for selecting options.
Q_DECLARE_JNI_CLASS(MotionEvent, "android/view/MotionEvent")
static QString getDisplayName(QJniObject zone, jint style, jboolean dst, const QLocale &locale)