001/* *******************************************
002// Copyright 2010-2015, Anthony Hand
003//
004//
005// File version 2015.05.13 (May 13, 2015)
006// Updates:
007//  - Moved MobileESP to GitHub. https://github.com/ahand/mobileesp
008//  - Opera Mobile/Mini browser has the same UA string on multiple platforms and doesn't differentiate phone vs. tablet.
009//      - Removed DetectOperaAndroidPhone(). This method is no longer reliable.
010//      - Removed DetectOperaAndroidTablet(). This method is no longer reliable.
011//  - Added support for Windows Phone 10: variable and DetectWindowsPhone10()
012//  - Updated DetectWindowsPhone() to include WP10.
013//  - Added support for Firefox OS.
014//      - A variable plus DetectFirefoxOS(), DetectFirefoxOSPhone(), DetectFirefoxOSTablet()
015//      - NOTE: Firefox doesn't add UA tokens to definitively identify Firefox OS vs. their browsers on other mobile platforms.
016//  - Added support for Sailfish OS. Not enough info to add a tablet detection method at this time.
017//      - A variable plus DetectSailfish(), DetectSailfishPhone()
018//  - Added support for Ubuntu Mobile OS.
019//      - DetectUbuntu(), DetectUbuntuPhone(), DetectUbuntuTablet()
020//  - Added support for 2 smart TV OSes. They lack browsers but do have WebViews for use by HTML apps.
021//      - One variable for Samsung Tizen TVs, plus DetectTizenTV()
022//      - One variable for LG WebOS TVs, plus DetectWebOSTV()
023//  - Updated DetectTizen(). Now tests for "mobile" to disambiguate from Samsung Smart TVs
024//  - Removed variables for obsolete devices: deviceHtcFlyer, deviceXoom.
025//  - Updated DetectAndroid(). No longer has a special test case for the HTC Flyer tablet.
026//  - Updated DetectAndroidPhone().
027//      - Updated internal detection code for Android.
028//      - No longer has a special test case for the HTC Flyer tablet.
029//      - Checks against DetectOperaMobile() on Android and reports here if relevant.
030//  - Updated DetectAndroidTablet().
031//      - No longer has a special test case for the HTC Flyer tablet.
032//      - Checks against DetectOperaMobile() on Android to exclude it from here.
033//  - DetectMeego(): Changed definition for this method. Now detects any Meego OS device, not just phones.
034//  - DetectMeegoPhone(): NEW. For Meego phones. Ought to detect Opera browsers on Meego, as well.
035//  - DetectTierIphone(): Added support for phones running Sailfish, Ubuntu and Firefox Mobile.
036//  - DetectTierTablet(): Added support for tablets running Ubuntu and Firefox Mobile.
037//  - DetectSmartphone(): Added support for Meego phones.
038//  - Refactored the detection logic in DetectMobileQuick() and DetectMobileLong().
039//      - Moved a few detection tests for older browsers to Long.
040//
041//
042//
043// LICENSE INFORMATION
044// Licensed under the Apache License, Version 2.0 (the "License");
045// you may not use this file except in compliance with the License.
046// You may obtain a copy of the License at
047//        http://www.apache.org/licenses/LICENSE-2.0
048// Unless required by applicable law or agreed to in writing,
049// software distributed under the License is distributed on an
050// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
051// either express or implied. See the License for the specific
052// language governing permissions and limitations under the License.
053//
054//
055// ABOUT THIS PROJECT
056//   Project Owner: Anthony Hand
057//   Email: anthony.hand@gmail.com
058//   Web Site: http://www.mobileesp.com
059//   Source Files: https://github.com/ahand/mobileesp
060//
061//   Versions of this code are available for:
062//      PHP, JavaScript, Java, ASP.NET (C#), and Ruby
063//
064// *******************************************
065 */
066
067package org.opencms.jsp.util;
068
069/**
070 * The DetectSmartPhone class encapsulates information about
071 *   a browser's connection to your web site.
072 *   You can use it to find out whether the browser asking for
073 *   your site's content is probably running on a mobile device.
074 *   The methods were written so you can be as granular as you want.
075 *   For example, enquiring whether it's as specific as an iPod Touch or
076 *   as general as a smartphone class device.
077 *   The object's methods return true, or false.
078 */
079@SuppressWarnings("all") // keeps the external code as unchanged as possible
080public class UAgentInfo {
081    // User-Agent and Accept HTTP request headers
082
083    // Initialize some initial smartphone string variables.
084    public static final String engineWebKit = "webkit";
085    public static final String deviceIphone = "iphone";
086
087    public static final String deviceIpod = "ipod";
088    public static final String deviceIpad = "ipad";
089    public static final String deviceMacPpc = "macintosh"; //Used for disambiguation
090    public static final String deviceAndroid = "android";
091    public static final String deviceGoogleTV = "googletv";
092    public static final String deviceWinPhone7 = "windows phone os 7";
093    public static final String deviceWinPhone8 = "windows phone 8";
094    public static final String deviceWinPhone10 = "windows phone 10";
095    public static final String deviceWinMob = "windows ce";
096    public static final String deviceWindows = "windows";
097
098    public static final String deviceIeMob = "iemobile";
099
100    public static final String devicePpc = "ppc"; //Stands for PocketPC
101    public static final String enginePie = "wm5 pie"; //An old Windows Mobile
102    public static final String deviceBB = "blackberry";
103    public static final String deviceBB10 = "bb10"; //For the new BB 10 OS
104
105    public static final String vndRIM = "vnd.rim"; //Detectable when BB devices emulate IE or Firefox
106    public static final String deviceBBStorm = "blackberry95"; //Storm 1 and 2
107
108    public static final String deviceBBBold = "blackberry97"; //Bold 97x0 (non-touch)
109    public static final String deviceBBBoldTouch = "blackberry 99"; //Bold 99x0 (touchscreen)
110    public static final String deviceBBTour = "blackberry96"; //Tour
111    public static final String deviceBBCurve = "blackberry89"; //Curve 2
112    public static final String deviceBBCurveTouch = "blackberry 938"; //Curve Touch 9380
113    public static final String deviceBBTorch = "blackberry 98"; //Torch
114    public static final String deviceBBPlaybook = "playbook"; //PlayBook tablet
115    public static final String deviceSymbian = "symbian";
116
117    public static final String deviceS60 = "series60";
118    public static final String deviceS70 = "series70";
119    public static final String deviceS80 = "series80";
120    public static final String deviceS90 = "series90";
121    public static final String devicePalm = "palm";
122    public static final String deviceWebOS = "webos"; //For Palm devices
123    public static final String deviceWebOStv = "web0s"; //For LG TVs
124    public static final String deviceWebOShp = "hpwos"; //For HP's line of WebOS devices
125    public static final String deviceNuvifone = "nuvifone"; //Garmin Nuvifone
126    public static final String deviceBada = "bada"; //Samsung's Bada OS
127    public static final String deviceTizen = "tizen"; //Tizen OS
128
129    public static final String deviceMeego = "meego"; //Meego OS
130    public static final String deviceSailfish = "sailfish"; //Sailfish OS
131    public static final String deviceUbuntu = "ubuntu"; //Ubuntu Mobile OS
132    public static final String deviceKindle = "kindle"; //Amazon Kindle, eInk one
133    public static final String engineSilk = "silk-accelerated"; //Amazon's accelerated Silk browser for Kindle Fire
134
135    public static final String engineBlazer = "blazer"; //Old Palm
136    public static final String engineXiino = "xiino"; //Another old Palm
137    //Initialize variables for mobile-specific content.
138    public static final String vndwap = "vnd.wap";
139    public static final String wml = "wml";
140
141    //Initialize variables for other random devices and mobile browsers.
142    public static final String deviceTablet = "tablet"; //Generic term for slate and tablet devices
143    public static final String deviceBrew = "brew";
144    public static final String deviceDanger = "danger";
145    public static final String deviceHiptop = "hiptop";
146    public static final String devicePlaystation = "playstation";
147    public static final String devicePlaystationVita = "vita";
148
149    public static final String deviceNintendoDs = "nitro";
150    public static final String deviceNintendo = "nintendo";
151
152    public static final String deviceWii = "wii";
153    public static final String deviceXbox = "xbox";
154
155    public static final String deviceArchos = "archos";
156    public static final String engineFirefox = "firefox"; //For Firefox OS
157
158    public static final String engineOpera = "opera"; //Popular browser
159    public static final String engineNetfront = "netfront"; //Common embedded OS browser
160    public static final String engineUpBrowser = "up.browser"; //common on some phones
161    public static final String engineOpenWeb = "openweb"; //Transcoding by OpenWave server
162    public static final String deviceMidp = "midp"; //a mobile Java technology
163    public static final String uplink = "up.link";
164    public static final String engineTelecaQ = "teleca q"; //a modern feature phone browser
165    public static final String devicePda = "pda"; //some devices report themselves as PDAs
166    public static final String mini = "mini"; //Some mobile browsers put "mini" in their names.
167    public static final String mobile = "mobile"; //Some mobile browsers put "mobile" in their user agent strings.
168    public static final String mobi = "mobi"; //Some mobile browsers put "mobi" in their user agent strings.
169
170    //Smart TV strings
171    public static final String smartTV1 = "smart-tv"; //Samsung Tizen smart TVs
172    public static final String smartTV2 = "smarttv"; //LG WebOS smart TVs
173    //Use Maemo, Tablet, and Linux to test for Nokia"s Internet Tablets.
174    public static final String maemo = "maemo";
175    public static final String linux = "linux";
176    public static final String qtembedded = "qt embedded"; //for Sony Mylo
177    public static final String mylocom2 = "com2"; //for Sony Mylo also
178    //In some UserAgents, the only clue is the manufacturer.
179    public static final String manuSonyEricsson = "sonyericsson";
180    public static final String manuericsson = "ericsson";
181    public static final String manuSamsung1 = "sec-sgh";
182    public static final String manuSony = "sony";
183    public static final String manuHtc = "htc"; //Popular Android and WinMo manufacturer
184    //In some UserAgents, the only clue is the operator.
185    public static final String svcDocomo = "docomo";
186
187    public static final String svcKddi = "kddi";
188    public static final String svcVodafone = "vodafone";
189
190    //Disambiguation strings.
191    public static final String disUpdate = "update"; //pda vs. update
192    private String userAgent = "";
193    private String httpAccept = "";
194    // Let's store values for quickly accessing the same info multiple times.
195    public boolean initCompleted = false;
196
197    public boolean isWebkit = false; //Stores the result of DetectWebkit()
198    public boolean isMobilePhone = false; //Stores the result of DetectMobileQuick()
199    public boolean isIphone = false; //Stores the result of DetectIphone()
200    public boolean isAndroid = false; //Stores the result of DetectAndroid()
201    public boolean isAndroidPhone = false; //Stores the result of DetectAndroidPhone()
202
203    public boolean isTierTablet = false; //Stores the result of DetectTierTablet()
204    public boolean isTierIphone = false; //Stores the result of DetectTierIphone()
205    public boolean isTierRichCss = false; //Stores the result of DetectTierRichCss()
206
207    public boolean isTierGenericMobile = false; //Stores the result of DetectTierOtherPhones()
208
209    /**
210     * Initialize the userAgent and httpAccept variables
211     *
212     * @param userAgent the User-Agent header
213     * @param httpAccept the Accept header
214     */
215    public UAgentInfo(String userAgent, String httpAccept) {
216
217        if (userAgent != null) {
218            this.userAgent = userAgent.toLowerCase();
219        }
220        if (httpAccept != null) {
221            this.httpAccept = httpAccept.toLowerCase();
222        }
223
224        //Intialize key stored values.
225        initDeviceScan();
226    }
227
228    /**
229     * Detects if the current Amazon device is using the Silk Browser.
230     * Note: Typically used by the the Kindle Fire.
231     * @return detection of an Amazon Kindle Fire in Silk mode.
232     */
233    public boolean detectAmazonSilk() {
234
235        if (userAgent.indexOf(engineSilk) != -1) {
236            return true;
237        }
238        return false;
239    }
240
241    /**
242     * Detects *any* Android OS-based device: phone, tablet, and multi-media player.
243     * Also detects Google TV.
244     * @return detection of an Android device
245     */
246    public boolean detectAndroid() {
247
248        if ((userAgent.indexOf(deviceAndroid) != -1) || detectGoogleTV()) {
249            return true;
250        }
251
252        return false;
253    }
254
255    /**
256     * Detects if the current device is a (small-ish) Android OS-based device
257     * used for calling and/or multi-media (like a Samsung Galaxy Player).
258     * Google says these devices will have 'Android' AND 'mobile' in user agent.
259     * Ignores tablets (Honeycomb and later).
260     * @return  detection of an Android phone
261     */
262    public boolean detectAndroidPhone() {
263
264        //First, let's make sure we're on an Android device.
265        if (!detectAndroid()) {
266            return false;
267        }
268
269        //If it's Android and has 'mobile' in it, Google says it's a phone.
270        if (userAgent.indexOf(mobile) != -1) {
271            return true;
272        }
273
274        //Special check for Android devices with Opera Mobile/Mini. They should report here.
275        if (detectOperaMobile()) {
276            return true;
277        }
278
279        return false;
280    }
281
282    /**
283     * Detects if the current device is a (self-reported) Android tablet.
284     * Google says these devices will have 'Android' and NOT 'mobile' in their user agent.
285     * @return detection of an Android tablet
286     */
287    public boolean detectAndroidTablet() {
288
289        //First, let's make sure we're on an Android device.
290        if (!detectAndroid()) {
291            return false;
292        }
293
294        //Special check for Android devices with Opera Mobile/Mini. They should NOT report here.
295        if (detectOperaMobile()) {
296            return false;
297        }
298
299        //Otherwise, if it's Android and does NOT have 'mobile' in it, Google says it's a tablet.
300        if ((userAgent.indexOf(mobile) > -1)) {
301            return false;
302        } else {
303            return true;
304        }
305    }
306
307    /**
308     * Detects if the current device is an Android OS-based device and
309     * the browser is based on WebKit.
310     * @return detection of an Android WebKit browser
311     */
312    public boolean detectAndroidWebKit() {
313
314        if (detectAndroid() && detectWebkit()) {
315            return true;
316        }
317        return false;
318    }
319
320    /**
321     * Detects if the current device is an Archos media player/Internet tablet.
322     * @return detection of an Archos media player
323     */
324    public boolean detectArchos() {
325
326        if (userAgent.indexOf(deviceArchos) != -1) {
327            return true;
328        }
329        return false;
330    }
331
332    /**
333     * Detects a device running the Bada OS from Samsung.
334     * @return detection of a Bada device
335     */
336    public boolean detectBada() {
337
338        if (userAgent.indexOf(deviceBada) != -1) {
339            return true;
340        }
341        return false;
342    }
343
344    /**
345     * Detects if the current browser is any BlackBerry.
346     * Includes BB10 OS, but excludes the PlayBook.
347     * @return detection of Blackberry
348     */
349    public boolean detectBlackBerry() {
350
351        if ((userAgent.indexOf(deviceBB) != -1) || (httpAccept.indexOf(vndRIM) != -1)) {
352            return true;
353        }
354
355        if (detectBlackBerry10Phone()) {
356            return true;
357        }
358
359        return false;
360    }
361
362    /**
363     * Detects if the current browser is a BlackBerry 10 OS phone.
364     * Excludes tablets.
365     * @return detection of a Blackberry 10 device
366     */
367    public boolean detectBlackBerry10Phone() {
368
369        if ((userAgent.indexOf(deviceBB10) != -1) && (userAgent.indexOf(mobile) != -1)) {
370            return true;
371        }
372        return false;
373    }
374
375    /**
376     * Detects if the current browser is a BlackBerry device AND
377     *   has a more capable recent browser. Excludes the Playbook.
378     *   Examples, Storm, Bold, Tour, Curve2
379     *   Excludes the new BlackBerry OS 6 and 7 browser!!
380     * @return detection of a Blackberry device with a better browser
381     */
382    public boolean detectBlackBerryHigh() {
383
384        //Disambiguate for BlackBerry OS 6 or 7 (WebKit) browser
385        if (detectBlackBerryWebKit()) {
386            return false;
387        }
388        if (detectBlackBerry()) {
389            if (detectBlackBerryTouch()
390                || (userAgent.indexOf(deviceBBBold) != -1)
391                || (userAgent.indexOf(deviceBBTour) != -1)
392                || (userAgent.indexOf(deviceBBCurve) != -1)) {
393                return true;
394            } else {
395                return false;
396            }
397        } else {
398            return false;
399        }
400    }
401
402    /**
403     * Detects if the current browser is a BlackBerry device AND
404     *   has an older, less capable browser.
405     *   Examples: Pearl, 8800, Curve1
406     * @return detection of a Blackberry device with a poorer browser
407     */
408    public boolean detectBlackBerryLow() {
409
410        if (detectBlackBerry()) {
411            //Assume that if it's not in the High tier, then it's Low
412            if (detectBlackBerryHigh() || detectBlackBerryWebKit()) {
413                return false;
414            } else {
415                return true;
416            }
417        } else {
418            return false;
419        }
420    }
421
422    /**
423     * Detects if the current browser is on a BlackBerry tablet device.
424     *    Example: PlayBook
425     * @return detection of a Blackberry Tablet
426     */
427    public boolean detectBlackBerryTablet() {
428
429        if (userAgent.indexOf(deviceBBPlaybook) != -1) {
430            return true;
431        }
432        return false;
433    }
434
435    /**
436     * Detects if the current browser is a BlackBerry Touch
437     * device, such as the Storm, Torch, and Bold Touch. Excludes the Playbook.
438     * @return detection of a Blackberry touchscreen device
439     */
440    public boolean detectBlackBerryTouch() {
441
442        if (detectBlackBerry()
443            && ((userAgent.indexOf(deviceBBStorm) != -1)
444                || (userAgent.indexOf(deviceBBTorch) != -1)
445                || (userAgent.indexOf(deviceBBBoldTouch) != -1)
446                || (userAgent.indexOf(deviceBBCurveTouch) != -1))) {
447            return true;
448        }
449        return false;
450    }
451
452    /**
453     * Detects if the current browser is a BlackBerry device AND uses a
454     *    WebKit-based browser. These are signatures for the new BlackBerry OS 6.
455     *    Examples: Torch. Includes the Playbook.
456     * @return detection of a Blackberry device with WebKit browser
457     */
458    public boolean detectBlackBerryWebKit() {
459
460        if (detectBlackBerry() && (userAgent.indexOf(engineWebKit) != -1)) {
461            return true;
462        }
463        return false;
464    }
465
466    /**
467     * Detects whether the device is a Brew-powered device.
468     * @return detection of a Brew device
469     */
470    public boolean detectBrewDevice() {
471
472        if (userAgent.indexOf(deviceBrew) != -1) {
473            return true;
474        }
475        return false;
476    }
477
478    /**
479     * Detects the Danger Hiptop device.
480     * @return detection of a Danger Hiptop
481     */
482    public boolean detectDangerHiptop() {
483
484        if ((userAgent.indexOf(deviceDanger) != -1) || (userAgent.indexOf(deviceHiptop) != -1)) {
485            return true;
486        }
487        return false;
488    }
489
490    /**
491     * Detects a mobile device (probably) running the Firefox OS.
492     * @return detection of a Firefox OS mobile device
493     */
494    public boolean detectFirefoxOS() {
495
496        if (detectFirefoxOSPhone() || detectFirefoxOSTablet()) {
497            return true;
498        }
499
500        return false;
501    }
502
503    /**
504     * Detects a phone (probably) running the Firefox OS.
505     * @return detection of a Firefox OS phone
506     */
507    public boolean detectFirefoxOSPhone() {
508
509        //First, let's make sure we're NOT on another major mobile OS.
510        if (detectIos() || detectAndroid() || detectSailfish()) {
511            return false;
512        }
513
514        if ((userAgent.indexOf(engineFirefox) != -1) && (userAgent.indexOf(mobile) != -1)) {
515            return true;
516        }
517
518        return false;
519    }
520
521    /**
522     * Detects a tablet (probably) running the Firefox OS.
523     * @return detection of a Firefox OS tablet
524     */
525    public boolean detectFirefoxOSTablet() {
526
527        //First, let's make sure we're NOT on another major mobile OS.
528        if (detectIos() || detectAndroid() || detectSailfish()) {
529            return false;
530        }
531
532        if ((userAgent.indexOf(engineFirefox) != -1) && (userAgent.indexOf(deviceTablet) != -1)) {
533            return true;
534        }
535
536        return false;
537    }
538
539    /**
540     * Detects if the current device is an Internet-capable game console.
541     * Includes many handheld consoles.
542     * @return detection of any Game Console
543     */
544    public boolean detectGameConsole() {
545
546        if (detectSonyPlaystation() || detectNintendo() || detectXbox()) {
547            return true;
548        }
549        return false;
550    }
551
552    /**
553     * Detects if the current device is a handheld gaming device with
554     * a touchscreen and modern iPhone-class browser. Includes the Playstation Vita.
555     * @return detection of a handheld gaming device
556     */
557    public boolean detectGamingHandheld() {
558
559        if ((userAgent.indexOf(devicePlaystation) != -1) && (userAgent.indexOf(devicePlaystationVita) != -1)) {
560            return true;
561        }
562        return false;
563    }
564
565    /**
566     * Detects if the current browser is a
567     *    Garmin Nuvifone.
568     * @return detection of a Garmin Nuvifone
569     */
570    public boolean detectGarminNuvifone() {
571
572        if (userAgent.indexOf(deviceNuvifone) != -1) {
573            return true;
574        }
575        return false;
576    }
577
578    /**
579     * Detects if the current device is a GoogleTV.
580     * @return detection of GoogleTV
581     */
582    public boolean detectGoogleTV() {
583
584        if (userAgent.indexOf(deviceGoogleTV) != -1) {
585            return true;
586        }
587        return false;
588    }
589
590    /**
591     * Detects *any* iOS device: iPhone, iPod Touch, iPad.
592     * @return detection of an Apple iOS device
593     */
594    public boolean detectIos() {
595
596        if (detectIphoneOrIpod() || detectIpad()) {
597            return true;
598        }
599        return false;
600    }
601
602    /**
603     * Detects if the current device is an iPad tablet.
604     * @return detection of an iPad
605     */
606    public boolean detectIpad() {
607
608        if ((userAgent.indexOf(deviceIpad) != -1) && detectWebkit()) {
609            return true;
610        }
611        return false;
612    }
613
614    /**
615     * Detects if the current device is an iPhone.
616     * @return detection of an iPhone
617     */
618    public boolean detectIphone() {
619
620        // The iPad and iPod touch say they're an iPhone! So let's disambiguate.
621        if ((userAgent.indexOf(deviceIphone) != -1) && !detectIpad() && !detectIpod()) {
622            return true;
623        }
624        return false;
625    }
626
627    /**
628     * Detects if the current device is an iPhone or iPod Touch.
629     * @return detection of an iPhone or iPod Touch
630     */
631    public boolean detectIphoneOrIpod() {
632
633        //We repeat the searches here because some iPods may report themselves as an iPhone, which would be okay.
634        if ((userAgent.indexOf(deviceIphone) != -1) || (userAgent.indexOf(deviceIpod) != -1)) {
635            return true;
636        }
637        return false;
638    }
639
640    /**
641     * Detects if the current device is an iPod Touch.
642     * @return detection of an iPod Touch
643     */
644    public boolean detectIpod() {
645
646        if (userAgent.indexOf(deviceIpod) != -1) {
647            return true;
648        }
649        return false;
650    }
651
652    /**
653     * Detects if the current device is an Amazon Kindle (eInk devices only).
654     * Note: For the Kindle Fire, use the normal Android methods.
655     * @return detection of a Kindle
656     */
657    public boolean detectKindle() {
658
659        if ((userAgent.indexOf(deviceKindle) != -1) && !detectAndroid()) {
660            return true;
661        }
662        return false;
663    }
664
665    /**
666     * Detects if the current device is on one of the Maemo-based Nokia Internet Tablets.
667     * @return detection of a Maemo OS tablet
668     */
669    public boolean detectMaemoTablet() {
670
671        if (userAgent.indexOf(maemo) != -1) {
672            return true;
673        } else if ((userAgent.indexOf(linux) != -1)
674            && (userAgent.indexOf(deviceTablet) != -1)
675            && !detectWebOSTablet()
676            && !detectAndroid()) {
677            return true;
678        }
679        return false;
680    }
681
682    /**
683     * Detects a device running the Meego OS.
684     * @return detection of a Meego device
685     */
686    public boolean detectMeego() {
687
688        if (userAgent.indexOf(deviceMeego) != -1) {
689            return true;
690        }
691        return false;
692    }
693
694    /**
695     * Detects a phone running the Meego OS.
696     * @return detection of a Meego phone
697     */
698    public boolean detectMeegoPhone() {
699
700        if ((userAgent.indexOf(deviceMeego) != -1) && (userAgent.indexOf(mobi) != -1)) {
701            return true;
702        }
703        return false;
704    }
705
706    /**
707     * Detects if the current device supports MIDP, a mobile Java technology.
708     * @return detection of a MIDP mobile Java-capable device
709     */
710    public boolean detectMidpCapable() {
711
712        if ((userAgent.indexOf(deviceMidp) != -1) || (httpAccept.indexOf(deviceMidp) != -1)) {
713            return true;
714        }
715        return false;
716    }
717
718    /**
719     * The longer and more thorough way to detect for a mobile device.
720     *   Will probably detect most feature phones,
721     *   smartphone-class devices, Internet Tablets,
722     *   Internet-enabled game consoles, etc.
723     *   This ought to catch a lot of the more obscure and older devices, also --
724     *   but no promises on thoroughness!
725     * @return detection of any mobile device using the more thorough method
726     */
727    public boolean detectMobileLong() {
728
729        if (detectMobileQuick() || detectGameConsole()) {
730            return true;
731        }
732
733        if (detectDangerHiptop() || detectMaemoTablet() || detectSonyMylo() || detectArchos()) {
734            return true;
735        }
736
737        if ((userAgent.indexOf(devicePda) != -1) && (userAgent.indexOf(disUpdate) < 0)) //no index found
738        {
739            return true;
740        }
741
742        //Detect older phones from certain manufacturers and operators.
743        if ((userAgent.indexOf(uplink) != -1)
744            || (userAgent.indexOf(engineOpenWeb) != -1)
745            || (userAgent.indexOf(manuSamsung1) != -1)
746            || (userAgent.indexOf(manuSonyEricsson) != -1)
747            || (userAgent.indexOf(manuericsson) != -1)
748            || (userAgent.indexOf(svcDocomo) != -1)
749            || (userAgent.indexOf(svcKddi) != -1)
750            || (userAgent.indexOf(svcVodafone) != -1)) {
751            return true;
752        }
753
754        return false;
755    }
756
757    /**
758     *  Detects if the current device is a mobile device.
759     *  This method catches most of the popular modern devices.
760     *  Excludes Apple iPads and other modern tablets.
761     * @return detection of any mobile device using the quicker method
762     */
763    public boolean detectMobileQuick() {
764
765        //Let's exclude tablets
766        if (isTierTablet) {
767            return false;
768        }
769        //Most mobile browsing is done on smartphones
770        if (detectSmartphone()) {
771            return true;
772        }
773
774        //Catch-all for many mobile devices
775        if (userAgent.indexOf(mobile) != -1) {
776            return true;
777        }
778
779        if (detectOperaMobile()) {
780            return true;
781        }
782
783        //We also look for Kindle devices
784        if (detectKindle() || detectAmazonSilk()) {
785            return true;
786        }
787
788        if (detectWapWml() || detectMidpCapable() || detectBrewDevice()) {
789            return true;
790        }
791
792        if ((userAgent.indexOf(engineNetfront) != -1) || (userAgent.indexOf(engineUpBrowser) != -1)) {
793            return true;
794        }
795
796        return false;
797    }
798
799    /**
800     * Detects if the current device is a Nintendo game device.
801     * @return detection of Nintendo
802     */
803    public boolean detectNintendo() {
804
805        if ((userAgent.indexOf(deviceNintendo) != -1)
806            || (userAgent.indexOf(deviceWii) != -1)
807            || (userAgent.indexOf(deviceNintendoDs) != -1)) {
808            return true;
809        }
810        return false;
811    }
812
813    /**
814     * Detects Opera Mobile or Opera Mini.
815     * @return detection of an Opera browser for a mobile device
816     */
817    public boolean detectOperaMobile() {
818
819        if ((userAgent.indexOf(engineOpera) != -1)
820            && ((userAgent.indexOf(mini) != -1) || (userAgent.indexOf(mobi) != -1))) {
821            return true;
822        }
823        return false;
824    }
825
826    /**
827     * Detects if the current browser is on a PalmOS device.
828     * @return detection of a PalmOS device
829     */
830    public boolean detectPalmOS() {
831
832        //Most devices nowadays report as 'Palm', but some older ones reported as Blazer or Xiino.
833        if ((userAgent.indexOf(devicePalm) != -1)
834            || (userAgent.indexOf(engineBlazer) != -1)
835            || (userAgent.indexOf(engineXiino) != -1)) {
836            //Make sure it's not WebOS first
837            if (detectPalmWebOS()) {
838                return false;
839            } else {
840                return true;
841            }
842        }
843        return false;
844    }
845
846    /**
847     * Detects if the current browser is on a Palm device
848     *    running the new WebOS.
849     * @return detection of a Palm WebOS device
850     */
851    public boolean detectPalmWebOS() {
852
853        if (userAgent.indexOf(deviceWebOS) != -1) {
854            return true;
855        }
856        return false;
857    }
858
859    /**
860     * Detects if the current browser is the Symbian S60 Open Source Browser.
861     * @return detection of Symbian S60 Browser
862     */
863    public boolean detectS60OssBrowser() {
864
865        //First, test for WebKit, then make sure it's either Symbian or S60.
866        if (detectWebkit() && ((userAgent.indexOf(deviceSymbian) != -1) || (userAgent.indexOf(deviceS60) != -1))) {
867            return true;
868        }
869        return false;
870    }
871
872    /**
873     * Detects a device running the Sailfish OS.
874     * @return detection of a Sailfish device
875     */
876    public boolean detectSailfish() {
877
878        if (userAgent.indexOf(deviceSailfish) != -1) {
879            return true;
880        }
881        return false;
882    }
883
884    /**
885     * Detects a phone running the Sailfish OS.
886     * @return detection of a Sailfish phone
887     */
888    public boolean detectSailfishPhone() {
889
890        if (detectSailfish() && (userAgent.indexOf(mobile) != -1)) {
891            return true;
892        }
893
894        return false;
895    }
896
897    /**
898     * Check to see whether the device is any device
899     *   in the 'smartphone' category.
900     * @return detection of a general smartphone device
901     */
902    public boolean detectSmartphone() {
903
904        //Exclude duplicates from TierIphone
905        return (detectTierIphone()
906            || detectS60OssBrowser()
907            || detectSymbianOS()
908            || detectWindowsMobile()
909            || detectBlackBerry()
910            || detectMeegoPhone()
911            || detectPalmOS());
912    }
913
914    /**
915     * Detects if the current browser is a Sony Mylo device.
916     * @return detection of a Sony Mylo device
917     */
918    public boolean detectSonyMylo() {
919
920        if ((userAgent.indexOf(manuSony) != -1)
921            && ((userAgent.indexOf(qtembedded) != -1) || (userAgent.indexOf(mylocom2) != -1))) {
922            return true;
923        }
924        return false;
925    }
926
927    /**
928     * Detects if the current device is a Sony Playstation.
929     * @return detection of Sony Playstation
930     */
931    public boolean detectSonyPlaystation() {
932
933        if (userAgent.indexOf(devicePlaystation) != -1) {
934            return true;
935        }
936        return false;
937    }
938
939    /**
940     *
941     * Detects if the current device is any Symbian OS-based device,
942     *   including older S60, Series 70, Series 80, Series 90, and UIQ,
943     *   or other browsers running on these devices.
944     * @return detection of SymbianOS
945     */
946    public boolean detectSymbianOS() {
947
948        if ((userAgent.indexOf(deviceSymbian) != -1)
949            || (userAgent.indexOf(deviceS60) != -1)
950            || (userAgent.indexOf(deviceS70) != -1)
951            || (userAgent.indexOf(deviceS80) != -1)
952            || (userAgent.indexOf(deviceS90) != -1)) {
953            return true;
954        }
955        return false;
956    }
957
958    /**
959     * The quick way to detect for a tier of devices.
960     *   This method detects for devices which can
961     *   display iPhone-optimized web content.
962     *   Includes iPhone, iPod Touch, Android, Windows Phone 7 and 8, BB10, WebOS, Playstation Vita, etc.
963     * @return detection of any device in the iPhone/Android/Windows Phone/BlackBerry/WebOS Tier
964     */
965    public boolean detectTierIphone() {
966
967        if (detectIphoneOrIpod()
968            || detectAndroidPhone()
969            || detectWindowsPhone()
970            || detectBlackBerry10Phone()
971            || (detectBlackBerryWebKit() && detectBlackBerryTouch())
972            || detectPalmWebOS()
973            || detectBada()
974            || detectTizen()
975            || detectFirefoxOSPhone()
976            || detectSailfishPhone()
977            || detectUbuntuPhone()
978            || detectGamingHandheld()) {
979            return true;
980        }
981        return false;
982    }
983
984    /**
985     * The quick way to detect for a tier of devices.
986     *   This method detects for all other types of phones,
987     *   but excludes the iPhone and RichCSS Tier devices.
988     * @return detection of a mobile device in the less capable tier
989     */
990    public boolean detectTierOtherPhones() {
991
992        //Exclude devices in the other 2 categories
993        if (detectMobileLong() && !detectTierIphone() && !detectTierRichCss()) {
994            return true;
995        }
996        return false;
997    }
998
999    /**
1000     * The quick way to detect for a tier of devices.
1001     *   This method detects for devices which are likely to be capable
1002     *   of viewing CSS content optimized for the iPhone,
1003     *   but may not necessarily support JavaScript.
1004     *   Excludes all iPhone Tier devices.
1005     * @return detection of any device in the 'Rich CSS' Tier
1006     */
1007    public boolean detectTierRichCss() {
1008
1009        boolean result = false;
1010        //The following devices are explicitly ok.
1011        //Note: 'High' BlackBerry devices ONLY
1012        if (detectMobileQuick()) {
1013
1014            //Exclude iPhone Tier and e-Ink Kindle devices.
1015            if (!detectTierIphone() && !detectKindle()) {
1016
1017                //The following devices are explicitly ok.
1018                //Note: 'High' BlackBerry devices ONLY
1019                //Older Windows 'Mobile' isn't good enough for iPhone Tier.
1020                if (detectWebkit()
1021                    || detectS60OssBrowser()
1022                    || detectBlackBerryHigh()
1023                    || detectWindowsMobile()
1024                    || (userAgent.indexOf(engineTelecaQ) != -1)) {
1025                    result = true;
1026                } // if detectWebkit()
1027            } //if !detectTierIphone()
1028        } //if detectMobileQuick()
1029        return result;
1030    }
1031
1032    /**
1033     * The quick way to detect for a tier of devices.
1034     *   This method detects for the new generation of
1035     *   HTML 5 capable, larger screen tablets.
1036     *   Includes iPad, Android (e.g., Xoom), BB Playbook, WebOS, etc.
1037     * @return detection of any device in the Tablet Tier
1038     */
1039    public boolean detectTierTablet() {
1040
1041        if (detectIpad()
1042            || detectAndroidTablet()
1043            || detectBlackBerryTablet()
1044            || detectFirefoxOSTablet()
1045            || detectUbuntuTablet()
1046            || detectWebOSTablet()) {
1047            return true;
1048        }
1049        return false;
1050    }
1051
1052    /**
1053     * Detects a device running the Tizen smartphone OS.
1054     * @return detection of a Tizen device
1055     */
1056    public boolean detectTizen() {
1057
1058        if ((userAgent.indexOf(deviceTizen) != -1) && (userAgent.indexOf(mobile) != -1)) {
1059            return true;
1060        }
1061        return false;
1062    }
1063
1064    /**
1065     * Detects if the current browser is on a Tizen smart TV.
1066     * @return detection of a Tizen smart TV
1067     */
1068    public boolean detectTizenTV() {
1069
1070        if ((userAgent.indexOf(deviceTizen) != -1) && (userAgent.indexOf(smartTV1) != -1)) {
1071            return true;
1072        }
1073        return false;
1074    }
1075
1076    /**
1077     * Detects a mobile device running the Ubuntu Mobile OS.
1078     * @return detection of an Ubuntu Mobile OS mobile device
1079     */
1080    public boolean detectUbuntu() {
1081
1082        if (detectUbuntuPhone() || detectUbuntuTablet()) {
1083            return true;
1084        }
1085
1086        return false;
1087    }
1088
1089    /**
1090     * Detects a phone running the Ubuntu Mobile OS.
1091     * @return detection of an Ubuntu Mobile OS phone
1092     */
1093    public boolean detectUbuntuPhone() {
1094
1095        if ((userAgent.indexOf(deviceUbuntu) != -1) && (userAgent.indexOf(mobile) != -1)) {
1096            return true;
1097        }
1098
1099        return false;
1100    }
1101
1102    /**
1103     * Detects a tablet running the Ubuntu Mobile OS.
1104     * @return detection of an Ubuntu Mobile OS tablet
1105     */
1106    public boolean detectUbuntuTablet() {
1107
1108        if ((userAgent.indexOf(deviceUbuntu) != -1) && (userAgent.indexOf(deviceTablet) != -1)) {
1109            return true;
1110        }
1111
1112        return false;
1113    }
1114
1115    /**
1116     * Detects whether the device supports WAP or WML.
1117     * @return detection of a WAP- or WML-capable device
1118     */
1119    public boolean detectWapWml() {
1120
1121        if ((httpAccept.indexOf(vndwap) != -1) || (httpAccept.indexOf(wml) != -1)) {
1122            return true;
1123        }
1124        return false;
1125    }
1126
1127    /**
1128     * Detects if the current browser is based on WebKit.
1129     * @return detection of a WebKit browser
1130     */
1131    public boolean detectWebkit() {
1132
1133        if (userAgent.indexOf(engineWebKit) != -1) {
1134            return true;
1135        }
1136        return false;
1137    }
1138
1139    /**
1140     * Detects if the current browser is on an HP tablet running WebOS.
1141     * @return detection of an HP WebOS tablet
1142     */
1143    public boolean detectWebOSTablet() {
1144
1145        if ((userAgent.indexOf(deviceWebOShp) != -1) && (userAgent.indexOf(deviceTablet) != -1)) {
1146            return true;
1147        }
1148        return false;
1149    }
1150
1151    /**
1152     * Detects if the current browser is on a WebOS smart TV.
1153     * @return detection of a WebOS smart TV
1154     */
1155    public boolean detectWebOSTV() {
1156
1157        if ((userAgent.indexOf(deviceWebOStv) != -1) && (userAgent.indexOf(smartTV2) != -1)) {
1158            return true;
1159        }
1160        return false;
1161    }
1162
1163    /**
1164     * Detects if the current browser is a Windows Mobile device.
1165     * Excludes Windows Phone 7.x and 8 devices.
1166     * Focuses on Windows Mobile 6.xx and earlier.
1167     * @return detection of Windows Mobile
1168     */
1169    public boolean detectWindowsMobile() {
1170
1171        if (detectWindowsPhone()) {
1172            return false;
1173        }
1174        //Most devices use 'Windows CE', but some report 'iemobile'
1175        //  and some older ones report as 'PIE' for Pocket IE.
1176        //  We also look for instances of HTC and Windows for many of their WinMo devices.
1177        if ((userAgent.indexOf(deviceWinMob) != -1)
1178            || (userAgent.indexOf(deviceWinMob) != -1)
1179            || (userAgent.indexOf(deviceIeMob) != -1)
1180            || (userAgent.indexOf(enginePie) != -1)
1181            || ((userAgent.indexOf(manuHtc) != -1) && (userAgent.indexOf(deviceWindows) != -1))
1182            || (detectWapWml() && (userAgent.indexOf(deviceWindows) != -1))) {
1183            return true;
1184        }
1185
1186        //Test for Windows Mobile PPC but not old Macintosh PowerPC.
1187        if ((userAgent.indexOf(devicePpc) != -1) && !(userAgent.indexOf(deviceMacPpc) != -1)) {
1188            return true;
1189        }
1190
1191        return false;
1192    }
1193
1194    /**
1195     * Detects if the current browser is a Windows Phone 7.x, 8, or 10 device
1196     * @return detection of Windows Phone 7.x OR 8
1197     */
1198    public boolean detectWindowsPhone() {
1199
1200        if (detectWindowsPhone7() || detectWindowsPhone8() || detectWindowsPhone10()) {
1201            return true;
1202        }
1203        return false;
1204    }
1205
1206    /**
1207     * Detects a Windows Phone 10 device (in mobile browsing mode).
1208     * @return detection of Windows Phone 10
1209     */
1210    public boolean detectWindowsPhone10() {
1211
1212        if (userAgent.indexOf(deviceWinPhone10) != -1) {
1213            return true;
1214        }
1215        return false;
1216    }
1217
1218    /**
1219     * Detects a Windows Phone 7 device (in mobile browsing mode).
1220     * @return detection of Windows Phone 7
1221     */
1222    public boolean detectWindowsPhone7() {
1223
1224        if (userAgent.indexOf(deviceWinPhone7) != -1) {
1225            return true;
1226        }
1227        return false;
1228    }
1229
1230    /**
1231     * Detects a Windows Phone 8 device (in mobile browsing mode).
1232     * @return detection of Windows Phone 8
1233     */
1234    public boolean detectWindowsPhone8() {
1235
1236        if (userAgent.indexOf(deviceWinPhone8) != -1) {
1237            return true;
1238        }
1239        return false;
1240    }
1241
1242    /**
1243     * Detects if the current device is a Microsoft Xbox.
1244     * @return detection of Xbox
1245     */
1246    public boolean detectXbox() {
1247
1248        if (userAgent.indexOf(deviceXbox) != -1) {
1249            return true;
1250        }
1251        return false;
1252    }
1253
1254    /**
1255     * Return the lower case HTTP_ACCEPT
1256     * @return httpAccept
1257     */
1258    public String getHttpAccept() {
1259
1260        return httpAccept;
1261    }
1262
1263    //*****************************
1264    // Device Classes
1265    //*****************************
1266
1267    /**
1268     * Return whether the device is an Iphone or iPod Touch
1269     * @return isIphone
1270     */
1271    public boolean getIsIphone() {
1272
1273        return isIphone;
1274    }
1275
1276    /**
1277     * Return whether the device is a generic, less-capable mobile device.
1278     * @return isTierGenericMobile
1279     */
1280    public boolean getIsTierGenericMobile() {
1281
1282        return isTierGenericMobile;
1283    }
1284
1285    /**
1286     * Return whether the device is in the Iphone Tier.
1287     * @return isTierIphone
1288     */
1289    public boolean getIsTierIphone() {
1290
1291        return isTierIphone;
1292    }
1293
1294    //*****************************
1295    // For Mobile Web Site Design
1296    //*****************************
1297
1298    /**
1299     * Return whether the device is in the 'Rich CSS' tier of mobile devices.
1300     * @return isTierRichCss
1301     */
1302    public boolean getIsTierRichCss() {
1303
1304        return isTierRichCss;
1305    }
1306
1307    /**
1308     * Return whether the device is in the Tablet Tier.
1309     * @return isTierTablet
1310     */
1311    public boolean getIsTierTablet() {
1312
1313        return isTierTablet;
1314    }
1315
1316    /**
1317     * Return the lower case HTTP_USER_AGENT
1318     * @return userAgent
1319     */
1320    public String getUserAgent() {
1321
1322        return userAgent;
1323    }
1324
1325    /**
1326     * Initialize Key Stored Values.
1327     */
1328    public void initDeviceScan() {
1329
1330        //Save these properties to speed processing
1331        this.isWebkit = detectWebkit();
1332        this.isIphone = detectIphone();
1333        this.isAndroid = detectAndroid();
1334        this.isAndroidPhone = detectAndroidPhone();
1335
1336        //Generally, these tiers are the most useful for web development
1337        this.isMobilePhone = detectMobileQuick();
1338        this.isTierTablet = detectTierTablet();
1339        this.isTierIphone = detectTierIphone();
1340
1341        //Optional: Comment these out if you NEVER use them
1342        this.isTierRichCss = detectTierRichCss();
1343        this.isTierGenericMobile = detectTierOtherPhones();
1344
1345        this.initCompleted = true;
1346    }
1347}