mirror of
https://github.com/KDE/kdeconnect-android
synced 2025-08-31 06:05:12 +00:00
Migrate DeviceHelper to Kotlin
This commit is contained in:
@@ -131,7 +131,7 @@ public class MdnsDiscovery {
|
||||
// Each field (key + value) needs to be < 255 bytes. All the fields combined need to be < 1300 bytes.
|
||||
// Also, on Android Lollipop those fields aren't resolved.
|
||||
String deviceName = DeviceHelper.getDeviceName(context);
|
||||
String deviceType = DeviceHelper.getDeviceType(context).toString();
|
||||
String deviceType = DeviceHelper.getDeviceType().toString();
|
||||
String protocolVersion = Integer.toString(DeviceHelper.ProtocolVersion);
|
||||
serviceInfo.setAttribute("id", deviceId);
|
||||
serviceInfo.setAttribute("name", deviceName);
|
||||
|
@@ -1,163 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2014 Albert Vaca Cintora <albertvaka@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
*/
|
||||
|
||||
package org.kde.kdeconnect.Helpers;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Build;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
|
||||
import com.univocity.parsers.common.TextParsingException;
|
||||
import com.univocity.parsers.csv.CsvParser;
|
||||
import com.univocity.parsers.csv.CsvParserSettings;
|
||||
|
||||
import org.kde.kdeconnect.DeviceInfo;
|
||||
import org.kde.kdeconnect.DeviceType;
|
||||
import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper;
|
||||
import org.kde.kdeconnect.Plugins.PluginFactory;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
public class DeviceHelper {
|
||||
|
||||
public static final int ProtocolVersion = 7;
|
||||
|
||||
public static final String KEY_DEVICE_NAME_PREFERENCE = "device_name_preference";
|
||||
public static final String KEY_DEVICE_NAME_FETCHED_FROM_THE_INTERNET = "device_name_downloaded_preference";
|
||||
public static final String KEY_DEVICE_ID_PREFERENCE = "device_id_preference";
|
||||
|
||||
private static boolean fetchingName = false;
|
||||
|
||||
public static final String DEVICE_DATABASE = "https://storage.googleapis.com/play_public/supported_devices.csv";
|
||||
|
||||
private static boolean isTablet() {
|
||||
Configuration config = Resources.getSystem().getConfiguration();
|
||||
//This assumes that the values for the screen sizes are consecutive, so XXLARGE > XLARGE > LARGE
|
||||
return ((config.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE);
|
||||
}
|
||||
|
||||
private static boolean isTv(Context context) {
|
||||
int uiMode = context.getResources().getConfiguration().uiMode;
|
||||
return (uiMode & Configuration.UI_MODE_TYPE_MASK) == Configuration.UI_MODE_TYPE_TELEVISION;
|
||||
}
|
||||
|
||||
public static DeviceType getDeviceType(Context context) {
|
||||
if (isTv(context)) {
|
||||
return DeviceType.TV;
|
||||
} else if (isTablet()) {
|
||||
return DeviceType.TABLET;
|
||||
} else {
|
||||
return DeviceType.PHONE;
|
||||
}
|
||||
}
|
||||
|
||||
public static String getDeviceName(Context context) {
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
if (!preferences.contains(KEY_DEVICE_NAME_PREFERENCE)
|
||||
&& !preferences.getBoolean(KEY_DEVICE_NAME_FETCHED_FROM_THE_INTERNET, false)
|
||||
&& !fetchingName) {
|
||||
fetchingName = true;
|
||||
DeviceHelper.backgroundFetchDeviceName(context);
|
||||
return Build.MODEL;
|
||||
}
|
||||
return preferences.getString(KEY_DEVICE_NAME_PREFERENCE, Build.MODEL);
|
||||
}
|
||||
|
||||
private static void backgroundFetchDeviceName(final Context context) {
|
||||
ThreadHelper.execute(() -> {
|
||||
try {
|
||||
URL url = new URL(DEVICE_DATABASE);
|
||||
URLConnection connection = url.openConnection();
|
||||
|
||||
// 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.
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
preferences.edit().putBoolean(KEY_DEVICE_NAME_FETCHED_FROM_THE_INTERNET, true).apply();
|
||||
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_16))) {
|
||||
CsvParserSettings settings = new CsvParserSettings();
|
||||
settings.setHeaderExtractionEnabled(true);
|
||||
CsvParser parser = new CsvParser(settings);
|
||||
boolean found = false;
|
||||
for (String[] records : parser.iterate(reader)) {
|
||||
if (records.length < 4) {
|
||||
continue;
|
||||
}
|
||||
String buildModel = records[3];
|
||||
if (Build.MODEL.equalsIgnoreCase(buildModel)) {
|
||||
String deviceName = records[1];
|
||||
Log.i("DeviceHelper", "Got device name: " + deviceName);
|
||||
// Update the shared preference. Places that display the name should be listening to this change and update it
|
||||
setDeviceName(context, deviceName);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
Log.e("DeviceHelper", "Didn't find a device name for " + Build.MODEL);
|
||||
}
|
||||
}
|
||||
} catch(IOException | TextParsingException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
fetchingName = false;
|
||||
});
|
||||
}
|
||||
|
||||
public static void setDeviceName(Context context, String name) {
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
preferences.edit().putString(KEY_DEVICE_NAME_PREFERENCE, name).apply();
|
||||
}
|
||||
|
||||
|
||||
@SuppressLint("HardwareIds")
|
||||
public static void initializeDeviceId(Context context) {
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
Set<String> preferenceKeys = preferences.getAll().keySet();
|
||||
if (preferenceKeys.contains(KEY_DEVICE_ID_PREFERENCE)) {
|
||||
return; // We already have an ID
|
||||
}
|
||||
String deviceName;
|
||||
if (preferenceKeys.isEmpty()) {
|
||||
// For new installations, use random IDs
|
||||
Log.i("DeviceHelper", "No device ID found and this looks like a new installation, creating a random ID");
|
||||
deviceName = UUID.randomUUID().toString().replace('-','_');
|
||||
} else {
|
||||
// Use the ANDROID_ID as device ID for existing installations, for backwards compatibility
|
||||
Log.i("DeviceHelper", "No device ID found but this seems an existing installation, using the Android ID");
|
||||
deviceName = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
|
||||
}
|
||||
preferences.edit().putString(KEY_DEVICE_ID_PREFERENCE, deviceName).apply();
|
||||
}
|
||||
|
||||
public static String getDeviceId(Context context) {
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
return preferences.getString(KEY_DEVICE_ID_PREFERENCE, null);
|
||||
}
|
||||
|
||||
public static DeviceInfo getDeviceInfo(Context context) {
|
||||
return new DeviceInfo(getDeviceId(context),
|
||||
SslHelper.certificate,
|
||||
getDeviceName(context),
|
||||
DeviceHelper.getDeviceType(context),
|
||||
ProtocolVersion,
|
||||
PluginFactory.getIncomingCapabilities(),
|
||||
PluginFactory.getOutgoingCapabilities());
|
||||
}
|
||||
|
||||
}
|
163
src/org/kde/kdeconnect/Helpers/DeviceHelper.kt
Normal file
163
src/org/kde/kdeconnect/Helpers/DeviceHelper.kt
Normal file
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Albert Vaca Cintora <albertvaka@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
*/
|
||||
package org.kde.kdeconnect.Helpers
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import android.content.res.Resources
|
||||
import android.os.Build
|
||||
import android.preference.PreferenceManager
|
||||
import android.provider.Settings
|
||||
import android.util.Log
|
||||
import com.univocity.parsers.common.TextParsingException
|
||||
import com.univocity.parsers.csv.CsvParser
|
||||
import com.univocity.parsers.csv.CsvParserSettings
|
||||
import org.kde.kdeconnect.DeviceInfo
|
||||
import org.kde.kdeconnect.DeviceType
|
||||
import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper
|
||||
import org.kde.kdeconnect.Plugins.PluginFactory
|
||||
import java.io.BufferedReader
|
||||
import java.io.IOException
|
||||
import java.io.InputStreamReader
|
||||
import java.net.URL
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.util.UUID
|
||||
|
||||
object DeviceHelper {
|
||||
const val ProtocolVersion = 7
|
||||
|
||||
const val KEY_DEVICE_NAME_PREFERENCE = "device_name_preference"
|
||||
private const val KEY_DEVICE_NAME_FETCHED_FROM_THE_INTERNET = "device_name_downloaded_preference"
|
||||
private const val KEY_DEVICE_ID_PREFERENCE = "device_id_preference"
|
||||
|
||||
private var fetchingName = false
|
||||
|
||||
private const val DEVICE_DATABASE = "https://storage.googleapis.com/play_public/supported_devices.csv"
|
||||
|
||||
private val isTablet: Boolean by lazy {
|
||||
val config = Resources.getSystem().configuration
|
||||
//This assumes that the values for the screen sizes are consecutive, so XXLARGE > XLARGE > LARGE
|
||||
((config.screenLayout and Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE)
|
||||
}
|
||||
|
||||
private val isTv: Boolean by lazy {
|
||||
val uiMode = Resources.getSystem().configuration.uiMode
|
||||
(uiMode and Configuration.UI_MODE_TYPE_MASK) == Configuration.UI_MODE_TYPE_TELEVISION
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
val deviceType: DeviceType by lazy {
|
||||
if (isTv) {
|
||||
DeviceType.TV
|
||||
} else if (isTablet) {
|
||||
DeviceType.TABLET
|
||||
} else {
|
||||
DeviceType.PHONE
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getDeviceName(context: Context): String {
|
||||
val preferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
if (!preferences.contains(KEY_DEVICE_NAME_PREFERENCE)
|
||||
&& !preferences.getBoolean(KEY_DEVICE_NAME_FETCHED_FROM_THE_INTERNET, false)
|
||||
&& !fetchingName
|
||||
) {
|
||||
fetchingName = true
|
||||
backgroundFetchDeviceName(context)
|
||||
return Build.MODEL
|
||||
}
|
||||
return preferences.getString(KEY_DEVICE_NAME_PREFERENCE, Build.MODEL)!!
|
||||
}
|
||||
|
||||
private fun backgroundFetchDeviceName(context: Context) {
|
||||
ThreadHelper.execute {
|
||||
try {
|
||||
val url = URL(DEVICE_DATABASE)
|
||||
val connection = url.openConnection()
|
||||
|
||||
// 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.
|
||||
val preferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
preferences.edit().putBoolean(KEY_DEVICE_NAME_FETCHED_FROM_THE_INTERNET, true).apply()
|
||||
|
||||
BufferedReader(
|
||||
InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_16)
|
||||
).use { reader ->
|
||||
val settings = CsvParserSettings()
|
||||
settings.isHeaderExtractionEnabled = true
|
||||
val parser = CsvParser(settings)
|
||||
var found = false
|
||||
for (records in parser.iterate(reader)) {
|
||||
if (records.size < 4) {
|
||||
continue
|
||||
}
|
||||
val buildModel = records[3]
|
||||
if (Build.MODEL.equals(buildModel, ignoreCase = true)) {
|
||||
val deviceName = records[1]
|
||||
Log.i("DeviceHelper", "Got device name: $deviceName")
|
||||
// Update the shared preference. Places that display the name should be listening to this change and update it
|
||||
setDeviceName(context, deviceName)
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
Log.e("DeviceHelper", "Didn't find a device name for " + Build.MODEL)
|
||||
}
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: TextParsingException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
fetchingName = false
|
||||
}
|
||||
}
|
||||
|
||||
fun setDeviceName(context: Context, name: String) {
|
||||
val preferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
preferences.edit().putString(KEY_DEVICE_NAME_PREFERENCE, name).apply()
|
||||
}
|
||||
|
||||
fun initializeDeviceId(context: Context) {
|
||||
val preferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
val preferenceKeys: Set<String> = preferences.all.keys
|
||||
if (preferenceKeys.contains(KEY_DEVICE_ID_PREFERENCE)) {
|
||||
return // We already have an ID
|
||||
}
|
||||
@SuppressLint("HardwareIds")
|
||||
val deviceName = if (preferenceKeys.isEmpty()) {
|
||||
// For new installations, use random IDs
|
||||
Log.i("DeviceHelper","No device ID found and this looks like a new installation, creating a random ID")
|
||||
UUID.randomUUID().toString().replace('-', '_')
|
||||
} else {
|
||||
// Use the ANDROID_ID as device ID for existing installations, for backwards compatibility
|
||||
Log.i("DeviceHelper", "No device ID found but this seems an existing installation, using the Android ID")
|
||||
Settings.Secure.getString(context.contentResolver, Settings.Secure.ANDROID_ID)
|
||||
}
|
||||
preferences.edit().putString(KEY_DEVICE_ID_PREFERENCE, deviceName).apply()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getDeviceId(context: Context): String {
|
||||
val preferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
return preferences.getString(KEY_DEVICE_ID_PREFERENCE, null)!!
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getDeviceInfo(context: Context): DeviceInfo {
|
||||
return DeviceInfo(
|
||||
getDeviceId(context),
|
||||
SslHelper.certificate,
|
||||
getDeviceName(context),
|
||||
deviceType,
|
||||
ProtocolVersion,
|
||||
PluginFactory.getIncomingCapabilities(),
|
||||
PluginFactory.getOutgoingCapabilities()
|
||||
)
|
||||
}
|
||||
}
|
@@ -52,7 +52,7 @@ public class FindMyPhonePlugin extends Plugin {
|
||||
|
||||
@Override
|
||||
public @NonNull String getDisplayName() {
|
||||
switch (DeviceHelper.getDeviceType(context)) {
|
||||
switch (DeviceHelper.getDeviceType()) {
|
||||
case TV:
|
||||
return context.getString(R.string.findmyphone_title_tv);
|
||||
case TABLET:
|
||||
|
@@ -108,7 +108,7 @@ class MainActivity : AppCompatActivity(), OnSharedPreferenceChangeListener {
|
||||
// it can trigger a background fetch from the internet that will eventually update the preference
|
||||
PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(this)
|
||||
val deviceName = DeviceHelper.getDeviceName(this)
|
||||
mNavViewDeviceType?.setImageDrawable(DeviceHelper.getDeviceType(this).getIcon(this))
|
||||
mNavViewDeviceType?.setImageDrawable(DeviceHelper.deviceType.getIcon(this))
|
||||
mNavViewDeviceName.text = deviceName
|
||||
mNavigationView.setNavigationItemSelectedListener { menuItem: MenuItem ->
|
||||
mCurrentMenuEntry = menuItem.itemId
|
||||
|
Reference in New Issue
Block a user