-
Notifications
You must be signed in to change notification settings - Fork 17
/
UserAgent.java
301 lines (248 loc) · 9.77 KB
/
UserAgent.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
package com.indeed.web.useragents;
import com.google.common.base.MoreObjects;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import nl.bitwalker.useragentutils.Browser;
import nl.bitwalker.useragentutils.DeviceType;
import nl.bitwalker.useragentutils.OperatingSystem;
import nl.bitwalker.useragentutils.Version;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.servlet.http.HttpServletRequest;
import java.util.Iterator;
/**
* Wrapper around bitwalker UserAgentUtils that provides lots of helpful convenience methods
* for defining proctor rules.
*
* @see nl.bitwalker.useragentutils.UserAgent
* @author gaurav
* @author matts
*/
public class UserAgent {
@Nonnull
private final nl.bitwalker.useragentutils.UserAgent userAgent;
@Nonnull
private final VersionedOS os;
@Nonnull
private final UserAgentVersion version;
@Nonnull
private final String userAgentString;
@Deprecated // Use UserAgent#parseUserAgentString instead
public UserAgent(@Nonnull final String userAgentString) {
this.userAgentString = userAgentString;
this.userAgent = nl.bitwalker.useragentutils.UserAgent.parseUserAgentString(userAgentString);
this.os = VersionedOS.fromUserAgent(this);
this.version = UserAgentVersion.from(userAgent.getBrowserVersion());
}
private UserAgent(
@Nullable final String userAgentHeader,
@Nonnull final nl.bitwalker.useragentutils.UserAgent delegate,
@Nonnull final UserAgentVersion version
) {
this.userAgentString = Strings.nullToEmpty(userAgentHeader);
this.userAgent = delegate;
this.os = VersionedOS.fromUserAgent(this);
this.version = version;
}
@Nonnull
public Browser getBrowser() {
return userAgent.getBrowser();
}
@Nonnull
public static UserAgent extractUserAgent(@Nonnull final HttpServletRequest request) {
return newBuilder().setRequest(request).build();
}
@Nonnull
public static UserAgent parseUserAgentString(@Nullable final String userAgentString) {
return newBuilder().setUserAgentString(userAgentString).build();
}
@Nonnull
public static UserAgent extractUserAgentSafely(@Nonnull final HttpServletRequest request) {
return newBuilder().setRequest(request).buildSafely();
}
@Nonnull
public static UserAgent parseUserAgentStringSafely(@Nullable final String userAgentString) {
return newBuilder().setUserAgentString(userAgentString).buildSafely();
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
UserAgent temp = (UserAgent) o;
final nl.bitwalker.useragentutils.UserAgent other = temp.userAgent;
return userAgent.equals(other);
}
@Override
public int hashCode() {
return userAgent.hashCode();
}
/**
* Delegate method
* Detects the detailed version information of the browser. Depends on the userAgent to be available.
* Use it only after using UserAgent(String) or UserAgent.parseUserAgent(String).
* Returns UserAgent.UNKNOWN_VERSION if it can not detect the version information.
* @return Version
*/
@Nonnull
public Version getBrowserVersion() {
return MoreObjects.firstNonNull(userAgent.getBrowserVersion(), UserAgentVersion.UNKNOWN_VERSION);
}
@Nonnull
public UserAgentVersion getVersion() {
return version;
}
@Nonnull
public OperatingSystem getOperatingSystem() {
return MoreObjects.firstNonNull(userAgent.getOperatingSystem(), OperatingSystem.UNKNOWN);
}
@Nonnull
public VersionedOS getOS() {
// TODO This will eventually subsume and replace the less useful getOperatingSystem
return this.os;
}
@Nonnull
public DeviceType getDeviceType() {
return MoreObjects.firstNonNull(getOperatingSystem().getDeviceType(), DeviceType.UNKNOWN);
}
@Nonnull
public String getUserAgentString() {
return this.userAgentString;
}
/**
* Determines whether user agent is a tablet
* @return true if tablet
*/
public boolean isTablet() {
return DeviceType.TABLET.equals(getDeviceType()) ||
userAgentString.contains(NEXUS_7_SIGNATURE) ||
userAgentString.contains(KINDLE_FIRE_SIGNATURE);
}
public boolean isIOS() {
final OperatingSystem operatingSystemGroup = getOperatingSystem().getGroup();
return isMobileDevice() &&
OperatingSystem.IOS.equals(operatingSystemGroup);
}
public boolean isChromeForIOS() {
return isIOS() && userAgentString.contains("CriOS");
}
public boolean isAndroid() {
final OperatingSystem operatingSystemGroup = getOperatingSystem().getGroup();
return OperatingSystem.ANDROID.equals(operatingSystemGroup);
}
public boolean isWindowsPhone() {
return OperatingSystem.WINDOWS_MOBILE7.equals(getOperatingSystem());
}
public boolean isMobileDevice() {
return getOperatingSystem().isMobileDevice() || isTablet();
}
public boolean isSmartPhone() {
OperatingSystem operatingSystemGroup = getOperatingSystem().getGroup();
return isMobileDevice() &&
(OperatingSystem.IOS.equals(operatingSystemGroup)
|| OperatingSystem.ANDROID.equals(operatingSystemGroup)
|| OperatingSystem.WINDOWS_MOBILE7.equals(getOperatingSystem()));
}
/**
* Mobile devices that are not tablets are regarded as phones.
* It is difficult to distinguish non-cellular handhelds
* @return true if mobile device and not a tablet
*/
public boolean isPhone() {
return getOperatingSystem().isMobileDevice() && !isTablet();
}
public boolean isDumbPhone() {
return isPhone() && !isSmartPhone();
}
/**
* Returns whether a given agent is capable of uploading a file.
*
* Note that this is NOT limited to just images, and therefore iOS does not qualify.
*
* Works for any non mobile agent
* Works for Android devices running Android v2.1+.
* @return
*/
public boolean isFileUploadCapable() {
return !isMobileDevice() ||
(isAndroid()
&& meetsMinimumVersion(2, 1, getOS().getMajorVersion(), getOS().getMinorVersion()));
}
private boolean meetsMinimumVersion(final int minMajorVersion, final int minMinorVersion,
final int majorVersion, final int minorVersion) {
return (majorVersion > minMajorVersion) || (majorVersion == minMajorVersion && minorVersion >= minMinorVersion);
}
@Override
public String toString() {
return userAgent.toString();
}
private static final String NEXUS_7_SIGNATURE = "Nexus 7";
private static final String KINDLE_FIRE_SIGNATURE = "Kindle Fire";
private static final nl.bitwalker.useragentutils.UserAgent UNKNOWN_USER_AGENT =
new nl.bitwalker.useragentutils.UserAgent(OperatingSystem.UNKNOWN, Browser.UNKNOWN);
public static Builder newBuilder() {
return new Builder();
}
public static class Builder {
private HttpServletRequest request;
private String userAgentString;
private Builder() { }
public Builder setRequest(@Nonnull final HttpServletRequest request) {
this.request = request;
this.userAgentString = request.getHeader("User-Agent");
return this;
}
public Builder setUserAgentString(@Nullable final String userAgentString) {
this.userAgentString = userAgentString;
return this;
}
@Nullable
private static UserAgentVersion parseVersion(@Nullable final String versionString) {
if (null == versionString) {
return null;
}
final Iterator<String> splits = Splitter.on('.').split(versionString).iterator();
final int majorVersion = splits.hasNext() ? getIntegerParameter(splits.next(), -1) : -1;
final int minorVersion = splits.hasNext() ? getIntegerParameter(splits.next(), -1) : -1;
return new UserAgentVersion(versionString, majorVersion, minorVersion);
}
@Nonnull
public UserAgent build() {
if (null == userAgentString) {
return new UserAgent(null, UNKNOWN_USER_AGENT, UserAgentVersion.UNKNOWN);
}
@Nullable
final nl.bitwalker.useragentutils.UserAgent userAgent =
nl.bitwalker.useragentutils.UserAgent.parseUserAgentString(userAgentString);
final nl.bitwalker.useragentutils.UserAgent userAgentToUse = MoreObjects.firstNonNull(userAgent, UNKNOWN_USER_AGENT);
final UserAgentVersion version = UserAgentVersion.from(userAgent.getBrowserVersion());
return new UserAgent(userAgentString, userAgentToUse, version);
}
public UserAgent buildSafely() {
try {
return build();
} catch (RuntimeException e) {
return new UserAgent(userAgentString, UNKNOWN_USER_AGENT, UserAgentVersion.UNKNOWN);
}
}
}
/**
* reading valid integer parameter
*
* @param argument expected value
* @param defaultValue default value
* @return valid integer or default value
*/
protected static int getIntegerParameter(String argument, int defaultValue) {
try {
return argument != null
? Integer.parseInt(argument)
: defaultValue;
} catch (NumberFormatException e) {
return defaultValue;
}
}
}