mirror of
https://github.com/KDE/kdeconnect-android
synced 2025-09-03 23:55:08 +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.
|
// 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.
|
// Also, on Android Lollipop those fields aren't resolved.
|
||||||
String deviceName = DeviceHelper.getDeviceName(context);
|
String deviceName = DeviceHelper.getDeviceName(context);
|
||||||
String deviceType = DeviceHelper.getDeviceType(context).toString();
|
String deviceType = DeviceHelper.getDeviceType().toString();
|
||||||
String protocolVersion = Integer.toString(DeviceHelper.ProtocolVersion);
|
String protocolVersion = Integer.toString(DeviceHelper.ProtocolVersion);
|
||||||
serviceInfo.setAttribute("id", deviceId);
|
serviceInfo.setAttribute("id", deviceId);
|
||||||
serviceInfo.setAttribute("name", deviceName);
|
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
|
@Override
|
||||||
public @NonNull String getDisplayName() {
|
public @NonNull String getDisplayName() {
|
||||||
switch (DeviceHelper.getDeviceType(context)) {
|
switch (DeviceHelper.getDeviceType()) {
|
||||||
case TV:
|
case TV:
|
||||||
return context.getString(R.string.findmyphone_title_tv);
|
return context.getString(R.string.findmyphone_title_tv);
|
||||||
case TABLET:
|
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
|
// it can trigger a background fetch from the internet that will eventually update the preference
|
||||||
PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(this)
|
PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(this)
|
||||||
val deviceName = DeviceHelper.getDeviceName(this)
|
val deviceName = DeviceHelper.getDeviceName(this)
|
||||||
mNavViewDeviceType?.setImageDrawable(DeviceHelper.getDeviceType(this).getIcon(this))
|
mNavViewDeviceType?.setImageDrawable(DeviceHelper.deviceType.getIcon(this))
|
||||||
mNavViewDeviceName.text = deviceName
|
mNavViewDeviceName.text = deviceName
|
||||||
mNavigationView.setNavigationItemSelectedListener { menuItem: MenuItem ->
|
mNavigationView.setNavigationItemSelectedListener { menuItem: MenuItem ->
|
||||||
mCurrentMenuEntry = menuItem.itemId
|
mCurrentMenuEntry = menuItem.itemId
|
||||||
|
Reference in New Issue
Block a user