File indexing completed on 2025-02-02 04:47:49
0001 /* 0002 * SPDX-FileCopyrightText: 2014 Albert Vaca Cintora <albertvaka@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0005 */ 0006 0007 package org.kde.kdeconnect.Helpers; 0008 0009 import android.annotation.SuppressLint; 0010 import android.content.Context; 0011 import android.content.SharedPreferences; 0012 import android.content.res.Configuration; 0013 import android.content.res.Resources; 0014 import android.os.Build; 0015 import android.preference.PreferenceManager; 0016 import android.provider.Settings; 0017 import android.util.Log; 0018 0019 import com.univocity.parsers.common.TextParsingException; 0020 import com.univocity.parsers.csv.CsvParser; 0021 import com.univocity.parsers.csv.CsvParserSettings; 0022 0023 import org.kde.kdeconnect.DeviceInfo; 0024 import org.kde.kdeconnect.DeviceType; 0025 import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper; 0026 import org.kde.kdeconnect.Plugins.PluginFactory; 0027 0028 import java.io.BufferedReader; 0029 import java.io.IOException; 0030 import java.io.InputStreamReader; 0031 import java.net.URL; 0032 import java.net.URLConnection; 0033 import java.nio.charset.StandardCharsets; 0034 import java.util.Set; 0035 import java.util.UUID; 0036 0037 public class DeviceHelper { 0038 0039 public static final int ProtocolVersion = 7; 0040 0041 public static final String KEY_DEVICE_NAME_PREFERENCE = "device_name_preference"; 0042 public static final String KEY_DEVICE_NAME_FETCHED_FROM_THE_INTERNET = "device_name_downloaded_preference"; 0043 public static final String KEY_DEVICE_ID_PREFERENCE = "device_id_preference"; 0044 0045 private static boolean fetchingName = false; 0046 0047 public static final String DEVICE_DATABASE = "https://storage.googleapis.com/play_public/supported_devices.csv"; 0048 0049 private static boolean isTablet() { 0050 Configuration config = Resources.getSystem().getConfiguration(); 0051 //This assumes that the values for the screen sizes are consecutive, so XXLARGE > XLARGE > LARGE 0052 return ((config.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE); 0053 } 0054 0055 private static boolean isTv(Context context) { 0056 int uiMode = context.getResources().getConfiguration().uiMode; 0057 return (uiMode & Configuration.UI_MODE_TYPE_MASK) == Configuration.UI_MODE_TYPE_TELEVISION; 0058 } 0059 0060 public static DeviceType getDeviceType(Context context) { 0061 if (isTv(context)) { 0062 return DeviceType.TV; 0063 } else if (isTablet()) { 0064 return DeviceType.TABLET; 0065 } else { 0066 return DeviceType.PHONE; 0067 } 0068 } 0069 0070 public static String getDeviceName(Context context) { 0071 SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); 0072 if (!preferences.contains(KEY_DEVICE_NAME_PREFERENCE) 0073 && !preferences.getBoolean(KEY_DEVICE_NAME_FETCHED_FROM_THE_INTERNET, false) 0074 && !fetchingName) { 0075 fetchingName = true; 0076 DeviceHelper.backgroundFetchDeviceName(context); 0077 return Build.MODEL; 0078 } 0079 return preferences.getString(KEY_DEVICE_NAME_PREFERENCE, Build.MODEL); 0080 } 0081 0082 private static void backgroundFetchDeviceName(final Context context) { 0083 ThreadHelper.execute(() -> { 0084 try { 0085 URL url = new URL(DEVICE_DATABASE); 0086 URLConnection connection = url.openConnection(); 0087 0088 // If we get here we managed to download the file. Mark that as done so we don't try again even if we don't end up finding a name. 0089 SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); 0090 preferences.edit().putBoolean(KEY_DEVICE_NAME_FETCHED_FROM_THE_INTERNET, true).apply(); 0091 0092 try (BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_16))) { 0093 CsvParserSettings settings = new CsvParserSettings(); 0094 settings.setHeaderExtractionEnabled(true); 0095 CsvParser parser = new CsvParser(settings); 0096 boolean found = false; 0097 for (String[] records : parser.iterate(reader)) { 0098 if (records.length < 4) { 0099 continue; 0100 } 0101 String buildModel = records[3]; 0102 if (Build.MODEL.equalsIgnoreCase(buildModel)) { 0103 String deviceName = records[1]; 0104 Log.i("DeviceHelper", "Got device name: " + deviceName); 0105 // Update the shared preference. Places that display the name should be listening to this change and update it 0106 setDeviceName(context, deviceName); 0107 found = true; 0108 break; 0109 } 0110 } 0111 if (!found) { 0112 Log.e("DeviceHelper", "Didn't find a device name for " + Build.MODEL); 0113 } 0114 } 0115 } catch(IOException | TextParsingException e) { 0116 e.printStackTrace(); 0117 } 0118 fetchingName = false; 0119 }); 0120 } 0121 0122 public static void setDeviceName(Context context, String name) { 0123 SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); 0124 preferences.edit().putString(KEY_DEVICE_NAME_PREFERENCE, name).apply(); 0125 } 0126 0127 0128 @SuppressLint("HardwareIds") 0129 public static void initializeDeviceId(Context context) { 0130 SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); 0131 Set<String> preferenceKeys = preferences.getAll().keySet(); 0132 if (preferenceKeys.contains(KEY_DEVICE_ID_PREFERENCE)) { 0133 return; // We already have an ID 0134 } 0135 String deviceName; 0136 if (preferenceKeys.isEmpty()) { 0137 // For new installations, use random IDs 0138 Log.i("DeviceHelper", "No device ID found and this looks like a new installation, creating a random ID"); 0139 deviceName = UUID.randomUUID().toString().replace('-','_'); 0140 } else { 0141 // Use the ANDROID_ID as device ID for existing installations, for backwards compatibility 0142 Log.i("DeviceHelper", "No device ID found but this seems an existing installation, using the Android ID"); 0143 deviceName = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID); 0144 } 0145 preferences.edit().putString(KEY_DEVICE_ID_PREFERENCE, deviceName).apply(); 0146 } 0147 0148 public static String getDeviceId(Context context) { 0149 SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); 0150 return preferences.getString(KEY_DEVICE_ID_PREFERENCE, null); 0151 } 0152 0153 public static DeviceInfo getDeviceInfo(Context context) { 0154 return new DeviceInfo(getDeviceId(context), 0155 SslHelper.certificate, 0156 getDeviceName(context), 0157 DeviceHelper.getDeviceType(context), 0158 ProtocolVersion, 0159 PluginFactory.getIncomingCapabilities(), 0160 PluginFactory.getOutgoingCapabilities()); 0161 } 0162 0163 }