All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.github.apkcloud.devicedetector.ClientHints Maven / Gradle / Ivy

Go to download

通用设备检测库将解析任何UserAgent并检测浏览器、操作系统、使用的设备(桌面、平板、移动、电视、车载、游戏机等等)、品牌和型号。

There is a newer version: 1.0.7
Show newest version
package io.github.apkcloud.devicedetector;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class ClientHints {
    /**
     * 代表 `Sec-CH-UA-Arch` 头字段:底层架构的指令集
     */
    protected String architecture = "";

    /**
     * 代表 `Sec-CH-UA-Bitness` 头字段:底层架构的位数
     */
    protected String bitness = "";

    /**
     * 代表 `Sec-CH-UA-Mobile` 头字段:UserAgent 是否应该接收特定的 "mobile(移动)" UX(用户体验)
     */
    protected boolean mobile = false;

    /**
     * 代表 `Sec-CH-UA-Model` 头字段:UserAgent 的底层设备型号
     */
    protected String model = "";

    /**
     * 代表 `Sec-CH-UA-Platform` 头字段:平台的品牌
     */
    protected String platform = "";

    /**
     * 代表 `Sec-CH-UA-Platform-Version` 头字段:平台的主要版本
     */
    protected String platformVersion = "";

    /**
     * 代表 `Sec-CH-UA-Full-Version` 头字段:平台的主要版本
     */
    protected String uaFullVersion = "";

    /**
     * 代表 `Sec-CH-UA-Full-Version-List` 头字段:品牌列表中每个品牌的完整版本
     */
    protected List> fullVersionList = new ArrayList<>();

    /**
     * 代表 `x-requested-with` 头字段:Android app id
     */
    protected String app = "";

    /**
     * Constructor
     *
     * @param model           `Sec-CH-UA-Model` 头字段
     * @param platform        `Sec-CH-UA-Platform` 头字段
     * @param platformVersion `Sec-CH-UA-Platform-Version` 头字段
     * @param uaFullVersion   `Sec-CH-UA-Full-Version` 头字段
     * @param fullVersionList `Sec-CH-UA-Full-Version-List` 头字段
     * @param mobile          `Sec-CH-UA-Mobile` 头字段
     * @param architecture    `Sec-CH-UA-Arch` 头字段
     * @param bitness         `Sec-CH-UA-Bitness`
     * @param app             `HTTP_X-REQUESTED-WITH`
     */
    public ClientHints(String model, String platform, String platformVersion, String uaFullVersion, List> fullVersionList, boolean mobile, String architecture, String bitness, String app) // phpcs:ignore Generic.Files.LineLength
    {
        this.model = model;
        this.platform = platform;
        this.platformVersion = platformVersion;
        this.uaFullVersion = uaFullVersion;
        this.fullVersionList = fullVersionList;
        this.mobile = mobile;
        this.architecture = architecture;
        this.bitness = bitness;
        this.app = app;
    }

    /**
     * 直接允许访问受保护的属性的魔术方法
     *
     * @param variable
     * @return mixed
     * @throws \Exception
     */
    /*public function __get(string variable) {
        if (\property_exists(this, variable)){
            return this.variable;
        }

        throw new \Exception('Invalid ClientHint property requested.');
    }*/

    /**
     * 返回ClientHints是否存在
     *
     * @return boolean
     */
    public boolean isMobile() {
        return this.mobile;
    }

    /**
     * 返回架构
     *
     * @return String
     */
    public String getArchitecture(){
        return this.architecture;
    }

    /**
     * 返回位数
     *
     * @return String
     */
    public String getBitness(){
        return this.bitness;
    }

    /**
     * 返回设备型号
     *
     * @return String
     */
    public String getModel(){
        return this.model;
    }

    /**
     * 返回操作系统
     *
     * @return String
     */
    public String getOperatingSystem(){
        return this.platform;
    }

    /**
     * 返回操作系统版本
     *
     * @return String
     */
    public String getOperatingSystemVersion(){
        return this.platformVersion;
    }

    /**
     * 返回浏览器名称
     *
     * @return {@code Map | null}
     */
    public Map getBrandList() {
        Map brandMap = null;
        if (this.fullVersionList != null && this.fullVersionList.size() > 0) {
            List brands = fullVersionList.stream()
                    .map(map -> map.get("brand"))
                    .collect(Collectors.toList());

            List versions = fullVersionList.stream()
                    .map(map -> map.get("version"))
                    .collect(Collectors.toList());

            /*if (brands.size() == versions.size()) {
                for (int i = 0; i < brands.size(); i++) {
                    brandMap.put(brands.get(i), versions.get(i));
                }
                return brandMap;
            }*/

            brandMap = IntStream.range(0, Math.min(brands.size(), versions.size()))
                    .boxed()
                    .collect(Collectors.toMap(brands::get, versions::get));
        }

        return brandMap;
    }

    /**
     * 返回浏览器版本
     *
     * @return String
     */
    public String getBrandVersion() {
        if (uaFullVersion != null && !uaFullVersion.isEmpty()) {
            return uaFullVersion;
        }

        return "";
    }

    /**
     * 返回 Android 应用程序 ID
     *
     * @return String
     */
    public String getApp() {
        return app;
    }

    /**
     * 工厂方法,使用包含所有可用(客户端提示)标头的数组轻松实例化此类
     *
     * @param headers 包含所有标头信息的数组
     *
     * @return ClientHints
     */
    public static ClientHints factory(Map headers) {
        String model = "";
        String platform = "";
        String platformVersion = "";
        String uaFullVersion = "";
        String architecture = "";
        String bitness = "";
        String app = "";
        boolean mobile = false;
        List> fullVersionList = new ArrayList<>();

        for (Map.Entry entry : headers.entrySet()) {
            String name = entry.getKey();
            String value = entry.getValue();
            String regex;
            List> list;
            switch (name.toLowerCase().replace("_", "-")) {
                case "sec-ch-ua-arch": // CPU底层架构,例如ARM或x86等。Sec-CH-UA-Arch: "x86"
                    architecture = value.replace("\"","").trim();
                    break;
                case "sec-ch-ua-bitness": //CPU底层架构的位数,通常为64位或32位。Sec-CH-UA-Bitness: "64"
                    bitness = value.replace("\"","").trim();
                    break;
                case "sec-ch-ua-mobile": // 设备是否为移动设备 ?1 (true) ?0 (false)
                    mobile = value.contains("1");
                    break;
                case "sec-ch-ua-model": // 设备型号。Sec-CH-UA-Model: "Pixel 3 XL"
                    model = value.replace("\"","").trim();
                    break;
                case "sec-ch-ua-full-version": // 浏览器的完整版本号。Sec-CH-UA-Full-Version: "96.0.4664.110"
                    uaFullVersion = value.replace("\"","").trim();
                    break;
                case "sec-ch-ua-platform": // 所运行的平台或操作系统。Sec-CH-UA-Platform: "macOS"
                    platform = value.replace("\"","").trim();
                    break;
                case "sec-ch-ua-platform-version": // 操作系统的版本。Sec-CH-UA-Platform-Version: "10.0.0"
                    platformVersion = value.replace("\"","").trim();
                    break;
                case "sec-ch-ua": // 与浏览器相关的品牌和重要版本。Sec-CH-UA: " Not A;Brand";v="99", "Chromium";v="96", "Microsoft Edge";v="96"
                    if (!fullVersionList.isEmpty()) {
                        break;
                    }
                    // 仅当没有其他请求头时才使用此选项设置列表
                    list = parseFullVersionList(value);
                    if (!list.isEmpty()) {
                        fullVersionList = list;
                    }
                    break;
                case "sec-ch-ua-full-version-list": // 与浏览器相关的品牌和完整版本。Sec-CH-UA: " Not A;Brand";v="99", "Chromium";v="96", "Microsoft Edge";v="96"
                    list = parseFullVersionList(value);
                    if (!list.isEmpty()) {
                        fullVersionList = list;
                    }
                    break;
                case "x-requested-with":
                    if (!"xmlhttprequest".equalsIgnoreCase(value)) {
                        app = value;
                    }
                    break;
            }


        }

        return new ClientHints(model, platform, platformVersion, uaFullVersion, fullVersionList, mobile, architecture, bitness, app);
    }

    private static List> parseFullVersionList(String value) {
        String regex = "^\"([^\"]+)\"; ?v=\"([^\"]+)\"(?:, )?";
        List> list = new ArrayList<>();
        Matcher matcher = Pattern.compile(regex).matcher(value);
        while (matcher.find()) {
            Matcher finalMatcher = matcher;
            list.add(new HashMap<>() {
                {
                    put("brand", finalMatcher.group(1).trim());
                    put("version", finalMatcher.group(2).trim());
                }
            });
            value = value.substring(matcher.group(0).length());
            matcher = Pattern.compile(regex).matcher(value);
        }
        return list;
    }
}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy