mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge.git
synced 2026-07-31 07:44:24 +02:00
Add FileInstallerActivity
This commit is contained in:
@@ -212,6 +212,7 @@ android {
|
||||
buildFeatures {
|
||||
aidl = true
|
||||
buildConfig = true
|
||||
viewBinding = true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -273,6 +273,10 @@
|
||||
<activity
|
||||
android:name=".activities.FwAppInstallerActivity"
|
||||
android:label="@string/title_activity_fw_app_insaller"
|
||||
android:parentActivityName=".activities.ControlCenterv2" />
|
||||
<activity
|
||||
android:name=".activities.install.FileInstallerActivity"
|
||||
android:label="@string/title_activity_file_installer"
|
||||
android:parentActivityName=".activities.ControlCenterv2"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
|
||||
+24
-74
@@ -33,6 +33,7 @@ import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.app.NavUtils;
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||
@@ -46,11 +47,8 @@ import java.util.List;
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.adapter.ItemWithDetailsAdapter;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceManager;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.GenericItem;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ItemWithDetails;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
@@ -171,9 +169,12 @@ public class FwAppInstallerActivity extends AbstractGBActivity implements Instal
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_appinstaller);
|
||||
|
||||
GBDevice dev = getIntent().getParcelableExtra(GBDevice.EXTRA_DEVICE);
|
||||
if (dev != null) {
|
||||
device = dev;
|
||||
device = getIntent().getParcelableExtra(GBDevice.EXTRA_DEVICE);
|
||||
if (device == null) {
|
||||
// This activity does not support auto-discovery - see FileInstallerActivity for that
|
||||
GB.toast("No device provided to FwAppInstallerActivity", Toast.LENGTH_LONG, GB.ERROR);
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
if (savedInstanceState != null) {
|
||||
details = savedInstanceState.getParcelableArrayList(ITEM_DETAILS);
|
||||
@@ -207,13 +208,10 @@ public class FwAppInstallerActivity extends AbstractGBActivity implements Instal
|
||||
filter.addAction(GB.ACTION_SET_FINISHED);
|
||||
LocalBroadcastManager.getInstance(this).registerReceiver(receiver, filter);
|
||||
|
||||
installButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
setInstallEnabled(false);
|
||||
installHandler.onStartInstall(device);
|
||||
GBApplication.deviceService(device).onInstallApp(uri, Bundle.EMPTY);
|
||||
}
|
||||
installButton.setOnClickListener(v -> {
|
||||
setInstallEnabled(false);
|
||||
installHandler.onStartInstall(device);
|
||||
GBApplication.deviceService(device).onInstallApp(uri, Bundle.EMPTY);
|
||||
});
|
||||
|
||||
closeButton.setOnClickListener(v -> finish());
|
||||
@@ -222,80 +220,32 @@ public class FwAppInstallerActivity extends AbstractGBActivity implements Instal
|
||||
if (uri == null) { // For "share" intent
|
||||
uri = getIntent().getParcelableExtra(Intent.EXTRA_STREAM);
|
||||
}
|
||||
installHandler = findInstallHandlerFor(uri);
|
||||
|
||||
installHandler = device.getDeviceCoordinator().findInstallHandler(uri, this);
|
||||
|
||||
if (installHandler == null) {
|
||||
// Should never happen? at this point, we got here by installing to the device
|
||||
setInfoText(getString(R.string.installer_activity_unable_to_find_handler));
|
||||
} else {
|
||||
setInfoText(getString(R.string.installer_activity_wait_while_determining_status));
|
||||
return;
|
||||
}
|
||||
|
||||
List<GBDevice> selectedDevices = GBApplication.app().getDeviceManager().getSelectedDevices();
|
||||
if(selectedDevices.size() == 0){
|
||||
GB.toast(getString(R.string.open_fw_installer_connect_minimum_one_device), Toast.LENGTH_LONG, GB.ERROR);
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
if(selectedDevices.size() != 1){
|
||||
GB.toast(getString(R.string.open_fw_installer_connect_maximum_one_device), Toast.LENGTH_LONG, GB.ERROR);
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
device = selectedDevices.get(0);
|
||||
setInfoText(getString(R.string.installer_activity_wait_while_determining_status));
|
||||
|
||||
// needed to get the device
|
||||
if (device == null || !device.isConnected()) {
|
||||
if (!device.isConnected()) {
|
||||
if (mayConnect) {
|
||||
connect();
|
||||
} else {
|
||||
GBApplication.deviceService(device).requestDeviceInfo();
|
||||
}
|
||||
} else {
|
||||
GBApplication.deviceService(device).requestDeviceInfo();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(Bundle outState) {
|
||||
protected void onSaveInstanceState(@NonNull Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putParcelableArrayList(ITEM_DETAILS, details);
|
||||
}
|
||||
|
||||
private InstallHandler findInstallHandlerFor(Uri uri) {
|
||||
for (DeviceCoordinator coordinator : getAllCoordinatorsConnectedFirst()) {
|
||||
InstallHandler handler = coordinator.findInstallHandler(uri, this);
|
||||
if (handler != null) {
|
||||
LOG.info("Found install handler {} from {}", handler.getClass(), coordinator.getClass());
|
||||
return handler;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private List<DeviceCoordinator> getAllCoordinatorsConnectedFirst() {
|
||||
DeviceManager deviceManager = ((GBApplication) getApplicationContext()).getDeviceManager();
|
||||
List<DeviceCoordinator> connectedCoordinators = new ArrayList<>();
|
||||
List<DeviceCoordinator> allCoordinators = new ArrayList<>(DeviceType.values().length);
|
||||
for(DeviceType type : DeviceType.values()){
|
||||
allCoordinators.add(type.getDeviceCoordinator());
|
||||
}
|
||||
List<DeviceCoordinator> sortedCoordinators = new ArrayList<>(allCoordinators.size());
|
||||
|
||||
List<GBDevice> devices = deviceManager.getSelectedDevices();
|
||||
for(GBDevice connectedDevice : devices){
|
||||
if (connectedDevice.isConnected()) {
|
||||
DeviceCoordinator coordinator = connectedDevice.getDeviceCoordinator();
|
||||
if (coordinator != null) {
|
||||
connectedCoordinators.add(coordinator);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sortedCoordinators.addAll(connectedCoordinators);
|
||||
for (DeviceCoordinator coordinator : allCoordinators) {
|
||||
if (!connectedCoordinators.contains(coordinator)) {
|
||||
sortedCoordinators.add(coordinator);
|
||||
}
|
||||
}
|
||||
return sortedCoordinators;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(final MenuItem item) {
|
||||
final int itemId = item.getItemId();
|
||||
@@ -320,7 +270,7 @@ public class FwAppInstallerActivity extends AbstractGBActivity implements Instal
|
||||
@Override
|
||||
public void setPreview(@Nullable final Bitmap bitmap) {
|
||||
previewImage.setImageBitmap(bitmap);
|
||||
if (previewImage == null) {
|
||||
if (bitmap == null) {
|
||||
previewImage.setVisibility(View.GONE);
|
||||
} else {
|
||||
previewImage.setVisibility(View.VISIBLE);
|
||||
|
||||
+22
-34
@@ -19,22 +19,17 @@ package nodomain.freeyourgadget.gadgetbridge.activities;
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
|
||||
public class OpenFwAppInstallerActivity extends AbstractGBActivity {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(OpenFwAppInstallerActivity.class);
|
||||
private int READ_REQUEST_CODE = 42;
|
||||
private static final int READ_REQUEST_CODE = 42;
|
||||
private TextView label;
|
||||
private GBDevice device;
|
||||
|
||||
@Override
|
||||
@@ -43,38 +38,21 @@ public class OpenFwAppInstallerActivity extends AbstractGBActivity {
|
||||
Bundle extras = getIntent().getExtras();
|
||||
if (extras != null) {
|
||||
device = extras.getParcelable(GBDevice.EXTRA_DEVICE);
|
||||
} else {
|
||||
}
|
||||
if (device == null) {
|
||||
throw new IllegalArgumentException("Must provide a device when invoking this activity");
|
||||
}
|
||||
|
||||
setContentView(R.layout.activity_open_fw_app_installer);
|
||||
Button pickFileButton = findViewById(R.id.open_fw_installer_pick_button);
|
||||
TextView label = findViewById(R.id.open_fw_installer_no_device);
|
||||
label = findViewById(R.id.open_fw_installer_no_device);
|
||||
label.setText(String.format(getString(R.string.open_fw_installer_select_file), device.getAliasOrName()));
|
||||
|
||||
final List<GBDevice> devices = ((GBApplication) getApplicationContext()).getDeviceManager().getSelectedDevices();
|
||||
switch (devices.size()) {
|
||||
case 0:
|
||||
label.setText(R.string.open_fw_installer_connect_minimum_one_device);
|
||||
pickFileButton.setEnabled(false);
|
||||
break;
|
||||
case 1:
|
||||
label.setText(String.format(getString(R.string.open_fw_installer_select_file), device.getAliasOrName()));
|
||||
pickFileButton.setEnabled(true);
|
||||
break;
|
||||
default:
|
||||
label.setText(R.string.open_fw_installer_connect_maximum_one_device);
|
||||
pickFileButton.setEnabled(false);
|
||||
}
|
||||
|
||||
pickFileButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
intent.setType("*/*");
|
||||
startActivityForResult(intent, READ_REQUEST_CODE);
|
||||
}
|
||||
pickFileButton.setOnClickListener(view -> {
|
||||
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
intent.setType("*/*");
|
||||
startActivityForResult(intent, READ_REQUEST_CODE);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -82,7 +60,17 @@ public class OpenFwAppInstallerActivity extends AbstractGBActivity {
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent resultData) {
|
||||
super.onActivityResult(requestCode, resultCode, resultData);
|
||||
if (requestCode == READ_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
|
||||
Intent startIntent = new Intent(OpenFwAppInstallerActivity.this, FwAppInstallerActivity.class);
|
||||
final InstallHandler installHandler = device.getDeviceCoordinator().findInstallHandler(
|
||||
resultData.getData(),
|
||||
GBApplication.app().getApplicationContext()
|
||||
);
|
||||
|
||||
if (installHandler == null) {
|
||||
label.setText(R.string.fwinstaller_file_not_compatible_to_device);
|
||||
return;
|
||||
}
|
||||
|
||||
final Intent startIntent = new Intent(OpenFwAppInstallerActivity.this, installHandler.getInstallActivity());
|
||||
startIntent.putExtra(GBDevice.EXTRA_DEVICE, device);
|
||||
startIntent.setAction(Intent.ACTION_VIEW);
|
||||
startIntent.setDataAndType(resultData.getData(), null);
|
||||
|
||||
+28
-22
@@ -19,9 +19,11 @@ package nodomain.freeyourgadget.gadgetbridge.activities.appmanager;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||
|
||||
@@ -37,6 +39,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.app.NavUtils;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
@@ -44,16 +47,17 @@ import androidx.viewpager.widget.ViewPager;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.AbstractFragmentPagerAdapter;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBFragmentActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.FwAppInstallerActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
|
||||
|
||||
public class AppManagerActivity extends AbstractGBFragmentActivity {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(AppManagerActivity.class);
|
||||
private int READ_REQUEST_CODE = 42;
|
||||
private static final int READ_REQUEST_CODE = 42;
|
||||
|
||||
private GBDevice mGBDevice = null;
|
||||
|
||||
@@ -128,18 +132,16 @@ public class AppManagerActivity extends AbstractGBFragmentActivity {
|
||||
super(fm);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Fragment getItem(int position) {
|
||||
// getItem is called to instantiate the fragment for the given page.
|
||||
switch (enabledTabsList.get(position)) {
|
||||
case "cache":
|
||||
return new AppManagerFragmentCache();
|
||||
case "apps":
|
||||
return new AppManagerFragmentInstalledApps();
|
||||
case "watchfaces":
|
||||
return new AppManagerFragmentInstalledWatchfaces();
|
||||
}
|
||||
return null;
|
||||
return switch (enabledTabsList.get(position)) {
|
||||
case "cache" -> new AppManagerFragmentCache();
|
||||
case "apps" -> new AppManagerFragmentInstalledApps();
|
||||
case "watchfaces" -> new AppManagerFragmentInstalledWatchfaces();
|
||||
default -> throw new IllegalArgumentException("Unknown tab position " + position);
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -149,15 +151,12 @@ public class AppManagerActivity extends AbstractGBFragmentActivity {
|
||||
|
||||
@Override
|
||||
public CharSequence getPageTitle(int position) {
|
||||
switch (enabledTabsList.get(position)) {
|
||||
case "cache":
|
||||
return getString(R.string.appmanager_cached_watchapps_watchfaces);
|
||||
case "apps":
|
||||
return getString(R.string.appmanager_installed_watchapps);
|
||||
case "watchfaces":
|
||||
return getString(R.string.appmanager_installed_watchfaces);
|
||||
}
|
||||
return super.getPageTitle(position);
|
||||
return switch (enabledTabsList.get(position)) {
|
||||
case "cache" -> getString(R.string.appmanager_cached_watchapps_watchfaces);
|
||||
case "apps" -> getString(R.string.appmanager_installed_watchapps);
|
||||
case "watchfaces" -> getString(R.string.appmanager_installed_watchfaces);
|
||||
default -> super.getPageTitle(position);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,9 +207,16 @@ public class AppManagerActivity extends AbstractGBFragmentActivity {
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent resultData) {
|
||||
super.onActivityResult(requestCode, resultCode, resultData);
|
||||
if (requestCode == READ_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
|
||||
Intent startIntent = new Intent(AppManagerActivity.this, FwAppInstallerActivity.class);
|
||||
final Uri uri = resultData.getData();
|
||||
final InstallHandler installHandler = mGBDevice.getDeviceCoordinator().findInstallHandler(uri, this);
|
||||
if (installHandler == null) {
|
||||
GB.toast(getString(R.string.fwinstaller_file_not_compatible_to_device), Toast.LENGTH_LONG, GB.INFO);
|
||||
return;
|
||||
}
|
||||
final Intent startIntent = new Intent(AppManagerActivity.this, installHandler.getInstallActivity());
|
||||
startIntent.putExtra(GBDevice.EXTRA_DEVICE, mGBDevice);
|
||||
startIntent.setAction(Intent.ACTION_VIEW);
|
||||
startIntent.setDataAndType(resultData.getData(), null);
|
||||
startIntent.setDataAndType(uri, null);
|
||||
startActivity(startIntent);
|
||||
}
|
||||
}
|
||||
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.activities.install
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication
|
||||
import nodomain.freeyourgadget.gadgetbridge.R
|
||||
import nodomain.freeyourgadget.gadgetbridge.adapter.DeviceInstallAdapter
|
||||
import nodomain.freeyourgadget.gadgetbridge.databinding.ActivityFileInstallerBinding
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
class FileInstallerActivity : AppCompatActivity() {
|
||||
private val viewModel: FileInstallerViewModel by viewModels {
|
||||
FileInstallerViewModelFactory(GBApplication.app())
|
||||
}
|
||||
|
||||
private lateinit var binding: ActivityFileInstallerBinding
|
||||
private lateinit var deviceAdapter: DeviceInstallAdapter
|
||||
|
||||
private val handlers: MutableMap<GBDevice, InstallHandler> = mutableMapOf()
|
||||
private var currentUri: Uri? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivityFileInstallerBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
currentUri = intent.data ?: intent.getParcelableExtra(Intent.EXTRA_STREAM)
|
||||
|
||||
// Setup the RecyclerView
|
||||
deviceAdapter = DeviceInstallAdapter { selectedDevice ->
|
||||
launchInstallActivity(selectedDevice, handlers[selectedDevice], currentUri)
|
||||
}
|
||||
binding.recyclerViewDevices.apply {
|
||||
adapter = deviceAdapter
|
||||
layoutManager = LinearLayoutManager(this@FileInstallerActivity)
|
||||
}
|
||||
|
||||
viewModel.findCompatibleDevices(currentUri)
|
||||
viewModel.uiState.observe(this) { state ->
|
||||
updateUi(state)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateUi(state: InstallDeviceUiState) {
|
||||
// Hide everything initially, then show the relevant one
|
||||
binding.fileInstallerLoadingProgress.visibility = View.GONE
|
||||
binding.recyclerViewDevices.visibility = View.GONE
|
||||
binding.textInstallerStatus.visibility = View.GONE
|
||||
|
||||
when (state) {
|
||||
is InstallDeviceUiState.Loading -> {
|
||||
binding.fileInstallerLoadingProgress.visibility = View.VISIBLE
|
||||
LOG.debug("Loading compatible devices...")
|
||||
}
|
||||
is InstallDeviceUiState.Success -> {
|
||||
if (state.results.isEmpty()) { // Ideally this should be caught by Error state
|
||||
binding.textInstallerStatus.text = getString(R.string.fwinstaller_no_compatible_device_found, currentUri)
|
||||
binding.textInstallerStatus.visibility = View.VISIBLE
|
||||
} else {
|
||||
binding.recyclerViewDevices.visibility = View.VISIBLE
|
||||
handlers.clear()
|
||||
for (pair in state.results) {
|
||||
handlers.put(pair.first, pair.second)
|
||||
}
|
||||
deviceAdapter.submitList(state.results.map { pair -> pair.first })
|
||||
if (state.results.size == 1) {
|
||||
// Single device was found - start the install activity directly
|
||||
val (device, handler) = state.results[0]
|
||||
launchInstallActivity(device, handler, currentUri)
|
||||
}
|
||||
}
|
||||
}
|
||||
is InstallDeviceUiState.Error -> {
|
||||
binding.textInstallerStatus.text = state.message
|
||||
binding.textInstallerStatus.visibility = View.VISIBLE
|
||||
LOG.error("Error: ${state.message}")
|
||||
}
|
||||
is InstallDeviceUiState.NoUri -> {
|
||||
// Should never happen
|
||||
@SuppressLint("SetTextI18n")
|
||||
binding.textInstallerStatus.text = "No file URI provided to install."
|
||||
binding.textInstallerStatus.visibility = View.VISIBLE
|
||||
LOG.warn("No URI provided.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun launchInstallActivity(selectedDevice: GBDevice, installHandler: InstallHandler?, uri: Uri?) {
|
||||
if (uri == null) {
|
||||
Toast.makeText(this, "Cannot install without a file URI.", Toast.LENGTH_LONG).show()
|
||||
finish()
|
||||
return
|
||||
}
|
||||
|
||||
if (installHandler == null) {
|
||||
// This should never happen
|
||||
Toast.makeText(this, "Failed to find installHandler", Toast.LENGTH_LONG).show()
|
||||
finish()
|
||||
return
|
||||
}
|
||||
|
||||
LOG.info(
|
||||
"Launching install activity {} for {}",
|
||||
installHandler.installActivity.simpleName,
|
||||
selectedDevice
|
||||
)
|
||||
|
||||
val intent = Intent(this, installHandler.installActivity).apply {
|
||||
setDataAndType(uri, contentResolver.getType(uri))
|
||||
putExtra(GBDevice.EXTRA_DEVICE, selectedDevice)
|
||||
setAction(Intent.ACTION_VIEW)
|
||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK or
|
||||
Intent.FLAG_ACTIVITY_TASK_ON_HOME or
|
||||
Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||
}
|
||||
startActivity(intent)
|
||||
finish()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val LOG = LoggerFactory.getLogger(FileInstallerActivity::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// FileInstallerViewModelFactory remains the same
|
||||
class FileInstallerViewModelFactory(
|
||||
private val application: GBApplication
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
if (modelClass.isAssignableFrom(FileInstallerViewModel::class.java)) {
|
||||
return FileInstallerViewModel(application) as T
|
||||
}
|
||||
throw IllegalArgumentException("Unknown ViewModel class")
|
||||
}
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.activities.install
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication
|
||||
import nodomain.freeyourgadget.gadgetbridge.R
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
sealed class InstallDeviceUiState {
|
||||
object Loading : InstallDeviceUiState()
|
||||
data class Success(val results: List<Pair<GBDevice, InstallHandler>>) : InstallDeviceUiState()
|
||||
object NoUri : InstallDeviceUiState()
|
||||
data class Error(val message: String) : InstallDeviceUiState()
|
||||
}
|
||||
|
||||
class FileInstallerViewModel(private val application: GBApplication) : ViewModel() {
|
||||
private val _uiState = MutableLiveData<InstallDeviceUiState>()
|
||||
val uiState: LiveData<InstallDeviceUiState> = _uiState
|
||||
|
||||
fun findCompatibleDevices(uri: Uri?) {
|
||||
if (uri == null) {
|
||||
_uiState.value = InstallDeviceUiState.NoUri
|
||||
return
|
||||
}
|
||||
|
||||
_uiState.value = InstallDeviceUiState.Loading
|
||||
viewModelScope.launch {
|
||||
val results = withContext(Dispatchers.IO) {
|
||||
val compatibleDevices = mutableListOf<Pair<GBDevice, InstallHandler>>()
|
||||
for (device in getAllDeviceTypesConnectedFirst()) {
|
||||
val coordinator = device.deviceCoordinator
|
||||
try {
|
||||
val handler = coordinator.findInstallHandler(uri, application.applicationContext)
|
||||
if (handler != null) {
|
||||
LOG.debug(
|
||||
"Found compatible install handler {} for {}",
|
||||
handler.javaClass.simpleName,
|
||||
device
|
||||
)
|
||||
compatibleDevices.add(Pair(device, handler))
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
LOG.error("Error finding install handler for $device", e)
|
||||
}
|
||||
}
|
||||
compatibleDevices
|
||||
}
|
||||
|
||||
if (results.isNotEmpty()) {
|
||||
_uiState.value = InstallDeviceUiState.Success(results)
|
||||
} else {
|
||||
_uiState.value = InstallDeviceUiState.Error(
|
||||
application.getString(R.string.fwinstaller_no_compatible_device_found, uri)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getAllDeviceTypesConnectedFirst(): List<GBDevice> {
|
||||
return application.deviceManager.devices
|
||||
.sortedWith { d1, d2 ->
|
||||
when {
|
||||
d1.isConnected && !d2.isConnected -> -1
|
||||
d2.isConnected && !d1.isConnected -> 1
|
||||
else -> d1.aliasOrName.compareTo(d2.aliasOrName)
|
||||
}
|
||||
}
|
||||
.toList()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val LOG = LoggerFactory.getLogger(FileInstallerViewModel::class.java)
|
||||
}
|
||||
}
|
||||
+1
@@ -381,6 +381,7 @@ public class MusicManagerActivity extends AbstractGBActivity {
|
||||
public void onActivityResult(ActivityResult result) {
|
||||
if (result.getResultCode() == Activity.RESULT_OK && result.getData() != null) {
|
||||
Intent startIntent = new Intent(MusicManagerActivity.this, FwAppInstallerActivity.class);
|
||||
startIntent.putExtra(GBDevice.EXTRA_DEVICE, mGBDevice);
|
||||
startIntent.setAction(Intent.ACTION_VIEW);
|
||||
startIntent.setDataAndType(result.getData().getData(), null);
|
||||
startActivity(startIntent);
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.adapter
|
||||
|
||||
import android.graphics.ColorMatrix
|
||||
import android.graphics.ColorMatrixColorFilter
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import nodomain.freeyourgadget.gadgetbridge.databinding.ListItemDeviceInstallBinding
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice
|
||||
|
||||
class DeviceInstallAdapter(
|
||||
private val onItemClicked: (GBDevice) -> Unit
|
||||
) : ListAdapter<GBDevice, DeviceInstallAdapter.DeviceViewHolder>(GBDeviceDiffUtil()) {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DeviceViewHolder {
|
||||
val binding = ListItemDeviceInstallBinding.inflate(
|
||||
LayoutInflater.from(parent.context),
|
||||
parent,
|
||||
false
|
||||
)
|
||||
return DeviceViewHolder(binding)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: DeviceViewHolder, position: Int) {
|
||||
val item = getItem(position)
|
||||
holder.bind(item)
|
||||
}
|
||||
|
||||
inner class DeviceViewHolder(private val binding: ListItemDeviceInstallBinding) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
init {
|
||||
binding.root.setOnClickListener {
|
||||
val position = bindingAdapterPosition
|
||||
if (position != RecyclerView.NO_POSITION) {
|
||||
onItemClicked(getItem(position))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun bind(device: GBDevice) {
|
||||
binding.textViewDeviceName.text = device.aliasOrName
|
||||
binding.imageViewDeviceIcon.setImageResource(device.deviceCoordinator.defaultIconResource)
|
||||
if (!device.isConnected) {
|
||||
val colorMatrix = ColorMatrix()
|
||||
colorMatrix.setSaturation(0f)
|
||||
binding.imageViewDeviceIcon.colorFilter = ColorMatrixColorFilter(colorMatrix)
|
||||
} else {
|
||||
binding.imageViewDeviceIcon.colorFilter = null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -68,7 +68,6 @@ import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||
import androidx.recyclerview.widget.DiffUtil;
|
||||
import androidx.recyclerview.widget.ListAdapter;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
@@ -84,7 +83,6 @@ import com.google.android.material.snackbar.Snackbar;
|
||||
import com.jaredrummler.android.colorpicker.ColorPickerDialog;
|
||||
import com.jaredrummler.android.colorpicker.ColorPickerDialogListener;
|
||||
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -1569,21 +1567,6 @@ public class GBDeviceAdapterv2 extends ListAdapter<GBDevice, GBDeviceAdapterv2.V
|
||||
shortcutManager.removeDynamicShortcuts(Collections.singletonList(device.getAddress()));
|
||||
}
|
||||
|
||||
private static class GBDeviceDiffUtil extends DiffUtil.ItemCallback<GBDevice> {
|
||||
@Override
|
||||
public boolean areItemsTheSame(@NonNull GBDevice oldItem, @NonNull GBDevice newItem) {
|
||||
return new EqualsBuilder()
|
||||
.append(oldItem.getAddress(), newItem.getAddress())
|
||||
.append(oldItem.getName(), newItem.getName())
|
||||
.isEquals();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areContentsTheSame(@NonNull GBDevice oldItem, @NonNull GBDevice newItem) {
|
||||
return EqualsBuilder.reflectionEquals(oldItem, newItem);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A generator of stable IDs, given a string, since hashCode can easily have collisions.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/* Copyright (C) 2025 José Rebelo
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Gadgetbridge is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.adapter;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.DiffUtil;
|
||||
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
|
||||
class GBDeviceDiffUtil extends DiffUtil.ItemCallback<GBDevice> {
|
||||
@Override
|
||||
public boolean areItemsTheSame(@NonNull final GBDevice oldItem,
|
||||
@NonNull final GBDevice newItem) {
|
||||
return new EqualsBuilder()
|
||||
.append(oldItem.getAddress(), newItem.getAddress())
|
||||
.append(oldItem.getName(), newItem.getName())
|
||||
.isEquals();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areContentsTheSame(@NonNull final GBDevice oldItem,
|
||||
@NonNull final GBDevice newItem) {
|
||||
return EqualsBuilder.reflectionEquals(oldItem, newItem);
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,10 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices;
|
||||
|
||||
import android.app.Activity;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.InstallActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
|
||||
@@ -25,6 +29,9 @@ import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
*/
|
||||
public interface InstallHandler {
|
||||
|
||||
@NonNull
|
||||
Class<? extends Activity> getInstallActivity();
|
||||
|
||||
/**
|
||||
* Returns true if this handler is able to install the element.
|
||||
* #validateInstallation may only be called if this method returned true.
|
||||
|
||||
+10
@@ -17,9 +17,13 @@
|
||||
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.atcbleoepl;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.FwAppInstallerActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.InstallActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
@@ -33,6 +37,12 @@ public class ATCBLEOEPLInstallHandler implements InstallHandler {
|
||||
mUri = uri;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Class<? extends Activity> getInstallActivity() {
|
||||
return FwAppInstallerActivity.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return true;
|
||||
|
||||
+28
-1
@@ -17,25 +17,52 @@
|
||||
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.divoom;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
import android.provider.MediaStore;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.FwAppInstallerActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.InstallActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
|
||||
public class PixooInstallHandler implements InstallHandler {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(PixooInstallHandler.class);
|
||||
|
||||
private final Context mContext;
|
||||
private final Uri mUri;
|
||||
|
||||
private Bitmap incomingBitmap;
|
||||
|
||||
public PixooInstallHandler(Uri uri, Context context) {
|
||||
mContext = context;
|
||||
mUri = uri;
|
||||
|
||||
try {
|
||||
incomingBitmap = MediaStore.Images.Media.getBitmap(mContext.getContentResolver(), mUri);
|
||||
} catch (final IOException e) {
|
||||
LOG.error("Could not decode image", e);
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Class<? extends Activity> getInstallActivity() {
|
||||
return FwAppInstallerActivity.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return true;
|
||||
return incomingBitmap != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+12
-7
@@ -16,9 +16,12 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.garmin;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -28,6 +31,7 @@ import java.io.InputStream;
|
||||
import java.util.Optional;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.FwAppInstallerActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.InstallActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
|
||||
@@ -109,6 +113,12 @@ public class GarminFitFileInstallHandler implements InstallHandler {
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Class<? extends Activity> getInstallActivity() {
|
||||
return FwAppInstallerActivity.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
// If we got a fitParseException, the file is "valid" (a fit file) for this handler, but corrupted
|
||||
@@ -134,13 +144,12 @@ public class GarminFitFileInstallHandler implements InstallHandler {
|
||||
}
|
||||
|
||||
final DeviceCoordinator coordinator = device.getDeviceCoordinator();
|
||||
if (!(coordinator instanceof GarminCoordinator)) {
|
||||
if (!(coordinator instanceof GarminCoordinator garminCoordinator)) {
|
||||
LOG.warn("Coordinator is not a GarminCoordinator: {}", coordinator.getClass());
|
||||
installActivity.setInfoText(mContext.getString(R.string.fwapp_install_device_not_supported));
|
||||
installActivity.setInstallEnabled(false);
|
||||
return;
|
||||
}
|
||||
final GarminCoordinator garminCoordinator = (GarminCoordinator) coordinator;
|
||||
final boolean fileSupported = parseFitFile(installActivity, garminCoordinator, device);
|
||||
|
||||
if (!fileSupported) {
|
||||
@@ -166,13 +175,9 @@ public class GarminFitFileInstallHandler implements InstallHandler {
|
||||
return rawBytes;
|
||||
}
|
||||
|
||||
public FitFile getFitFile() {
|
||||
return fitFile;
|
||||
}
|
||||
|
||||
public FileType.FILETYPE getFileType() {
|
||||
return fileType;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean parseFitFile(final InstallActivity installActivity, final GarminCoordinator coordinator, final GBDevice device) {
|
||||
final String name;
|
||||
|
||||
+14
-5
@@ -16,9 +16,14 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.garmin;
|
||||
|
||||
import static nodomain.freeyourgadget.gadgetbridge.devices.vivomovehr.GarminCapability.COURSE_DOWNLOAD;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -27,6 +32,7 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.FwAppInstallerActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.InstallActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
|
||||
@@ -36,8 +42,6 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.GpxRouteF
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.UriHelper;
|
||||
|
||||
import static nodomain.freeyourgadget.gadgetbridge.devices.vivomovehr.GarminCapability.COURSE_DOWNLOAD;
|
||||
|
||||
public class GarminGpxRouteInstallHandler implements InstallHandler {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(GarminGpxRouteInstallHandler.class);
|
||||
|
||||
@@ -67,6 +71,12 @@ public class GarminGpxRouteInstallHandler implements InstallHandler {
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Class<? extends Activity> getInstallActivity() {
|
||||
return FwAppInstallerActivity.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return gpxRouteFileConverter != null;
|
||||
@@ -81,14 +91,13 @@ public class GarminGpxRouteInstallHandler implements InstallHandler {
|
||||
}
|
||||
|
||||
final DeviceCoordinator coordinator = device.getDeviceCoordinator();
|
||||
if (!(coordinator instanceof GarminCoordinator)) {
|
||||
if (!(coordinator instanceof GarminCoordinator garminCoordinator)) {
|
||||
LOG.warn("Coordinator is not a GarminCoordinator: {}", coordinator.getClass());
|
||||
installActivity.setInfoText(mContext.getString(R.string.fwapp_install_device_not_supported));
|
||||
installActivity.setInstallEnabled(false);
|
||||
return;
|
||||
}
|
||||
final GarminCoordinator garminCoordinator = (GarminCoordinator) coordinator;
|
||||
if (!garminCoordinator.supports(device, COURSE_DOWNLOAD)) {
|
||||
if (garminCoordinator.supports(device, COURSE_DOWNLOAD)) {
|
||||
installActivity.setInfoText(mContext.getString(R.string.fwapp_install_device_not_supported));
|
||||
installActivity.setInstallEnabled(false);
|
||||
return;
|
||||
|
||||
+11
-2
@@ -16,13 +16,17 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.garmin;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.FwAppInstallerActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.InstallActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
|
||||
@@ -42,6 +46,12 @@ public class GarminPrgFileInstallHandler implements InstallHandler {
|
||||
this.prgFile = new GarminPrgFile(uri, context);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Class<? extends Activity> getInstallActivity() {
|
||||
return FwAppInstallerActivity.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return prgFile.isValid();
|
||||
@@ -57,14 +67,13 @@ public class GarminPrgFileInstallHandler implements InstallHandler {
|
||||
}
|
||||
|
||||
final DeviceCoordinator coordinator = device.getDeviceCoordinator();
|
||||
if (!(coordinator instanceof GarminCoordinator)) {
|
||||
if (!(coordinator instanceof GarminCoordinator garminCoordinator)) {
|
||||
LOG.warn("Coordinator is not a GarminCoordinator: {}", coordinator.getClass());
|
||||
installActivity.setInfoText(mContext.getString(R.string.fwapp_install_device_not_supported));
|
||||
installActivity.setInstallEnabled(false);
|
||||
installActivity.setCloseEnabled(true);
|
||||
return;
|
||||
}
|
||||
final GarminCoordinator garminCoordinator = (GarminCoordinator) coordinator;
|
||||
|
||||
// FIXME this might not be the correct capability
|
||||
if (!garminCoordinator.supports(device, GarminCapability.CONNECTIQ_APP_MANAGEMENT)) {
|
||||
|
||||
+11
-2
@@ -16,9 +16,12 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.huami.zeppos;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -27,6 +30,7 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.FwAppInstallerActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.InstallActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
|
||||
@@ -71,6 +75,12 @@ public class ZeppOsAgpsInstallHandler implements InstallHandler {
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Class<? extends Activity> getInstallActivity() {
|
||||
return FwAppInstallerActivity.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return file != null && file.isValid();
|
||||
@@ -85,13 +95,12 @@ public class ZeppOsAgpsInstallHandler implements InstallHandler {
|
||||
}
|
||||
|
||||
final DeviceCoordinator coordinator = device.getDeviceCoordinator();
|
||||
if (!(coordinator instanceof ZeppOsCoordinator)) {
|
||||
if (!(coordinator instanceof ZeppOsCoordinator zeppOsCoordinator)) {
|
||||
LOG.warn("Coordinator is not a ZeppOsCoordinator: {}", coordinator.getClass());
|
||||
installActivity.setInfoText(mContext.getString(R.string.fwapp_install_device_not_supported));
|
||||
installActivity.setInstallEnabled(false);
|
||||
return;
|
||||
}
|
||||
final ZeppOsCoordinator zeppOsCoordinator = (ZeppOsCoordinator) coordinator;
|
||||
if (!zeppOsCoordinator.supportsAgpsUpdates()) {
|
||||
installActivity.setInfoText(mContext.getString(R.string.fwapp_install_device_not_supported));
|
||||
installActivity.setInstallEnabled(false);
|
||||
|
||||
+11
-2
@@ -16,9 +16,12 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.huami.zeppos;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -27,6 +30,7 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.FwAppInstallerActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.InstallActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
|
||||
@@ -71,6 +75,12 @@ public class ZeppOsGpxRouteInstallHandler implements InstallHandler {
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Class<? extends Activity> getInstallActivity() {
|
||||
return FwAppInstallerActivity.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return file != null && file.isValid();
|
||||
@@ -85,13 +95,12 @@ public class ZeppOsGpxRouteInstallHandler implements InstallHandler {
|
||||
}
|
||||
|
||||
final DeviceCoordinator coordinator = device.getDeviceCoordinator();
|
||||
if (!(coordinator instanceof ZeppOsCoordinator)) {
|
||||
if (!(coordinator instanceof ZeppOsCoordinator zeppOsCoordinator)) {
|
||||
LOG.warn("Coordinator is not a ZeppOsCoordinator: {}", coordinator.getClass());
|
||||
installActivity.setInfoText(mContext.getString(R.string.fwapp_install_device_not_supported));
|
||||
installActivity.setInstallEnabled(false);
|
||||
return;
|
||||
}
|
||||
final ZeppOsCoordinator zeppOsCoordinator = (ZeppOsCoordinator) coordinator;
|
||||
if (!zeppOsCoordinator.supportsGpxUploads(device)) {
|
||||
installActivity.setInfoText(mContext.getString(R.string.fwapp_install_device_not_supported));
|
||||
installActivity.setInstallEnabled(false);
|
||||
|
||||
+11
-2
@@ -16,13 +16,17 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.huami.zeppos;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.FwAppInstallerActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.InstallActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
|
||||
@@ -42,6 +46,12 @@ public class ZeppOsMapsInstallHandler implements InstallHandler {
|
||||
file = new ZeppOsMapsFile(uri, context);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Class<? extends Activity> getInstallActivity() {
|
||||
return FwAppInstallerActivity.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return file.isValid();
|
||||
@@ -57,14 +67,13 @@ public class ZeppOsMapsInstallHandler implements InstallHandler {
|
||||
}
|
||||
|
||||
final DeviceCoordinator coordinator = device.getDeviceCoordinator();
|
||||
if (!(coordinator instanceof ZeppOsCoordinator)) {
|
||||
if (!(coordinator instanceof ZeppOsCoordinator zeppOsCoordinator)) {
|
||||
LOG.warn("Coordinator is not a ZeppOsCoordinator: {}", coordinator.getClass());
|
||||
installActivity.setInfoText(mContext.getString(R.string.fwapp_install_device_not_supported));
|
||||
installActivity.setInstallEnabled(false);
|
||||
installActivity.setCloseEnabled(true);
|
||||
return;
|
||||
}
|
||||
final ZeppOsCoordinator zeppOsCoordinator = (ZeppOsCoordinator) coordinator;
|
||||
if (!zeppOsCoordinator.supportsMaps(device) || zeppOsCoordinator.supportsWifiHotspot(device)) {
|
||||
installActivity.setInfoText(mContext.getString(R.string.fwapp_install_device_not_supported));
|
||||
installActivity.setInstallEnabled(false);
|
||||
|
||||
+9
@@ -16,9 +16,11 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.huami.zeppos;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
@@ -29,6 +31,7 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.FwAppInstallerActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.InstallActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
|
||||
@@ -52,6 +55,12 @@ public class ZeppOsMusicInstallHandler implements InstallHandler {
|
||||
this.mAudioInfo = MusicUtils.audioInfoFromUri(context, uri);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Class<? extends Activity> getInstallActivity() {
|
||||
return FwAppInstallerActivity.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return mAudioInfo != null &&
|
||||
|
||||
+18
-7
@@ -17,21 +17,25 @@
|
||||
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.huawei;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.FwAppInstallerActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.InstallActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.GenericItem;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiAppManager;
|
||||
@@ -110,7 +114,7 @@ public class HuaweiInstallHandler implements InstallHandler {
|
||||
public void validateInstallation(InstallActivity installActivity, GBDevice device) {
|
||||
|
||||
final DeviceCoordinator coordinator = device.getDeviceCoordinator();
|
||||
if (!(coordinator instanceof HuaweiCoordinatorSupplier)) {
|
||||
if (!(coordinator instanceof HuaweiCoordinatorSupplier huaweiCoordinatorSupplier)) {
|
||||
LOG.warn("Coordinator is not a HuaweiCoordinatorSupplier: {}", coordinator.getClass());
|
||||
installActivity.setInstallEnabled(false);
|
||||
return;
|
||||
@@ -157,13 +161,16 @@ public class HuaweiInstallHandler implements InstallHandler {
|
||||
}
|
||||
|
||||
if (helper.isWatchface()) {
|
||||
final HuaweiCoordinatorSupplier huaweiCoordinatorSupplier = (HuaweiCoordinatorSupplier) coordinator;
|
||||
|
||||
HuaweiWatchfaceManager.WatchfaceDescription description = helper.getWatchfaceDescription();
|
||||
|
||||
HuaweiWatchfaceManager.Resolution resolution = new HuaweiWatchfaceManager.Resolution();
|
||||
String deviceScreen = String.format("%d*%d", huaweiCoordinatorSupplier.getHuaweiCoordinator().getHeight(),
|
||||
huaweiCoordinatorSupplier.getHuaweiCoordinator().getWidth());
|
||||
String deviceScreen = String.format(
|
||||
Locale.ROOT,
|
||||
"%d*%d",
|
||||
huaweiCoordinatorSupplier.getHuaweiCoordinator().getHeight(),
|
||||
huaweiCoordinatorSupplier.getHuaweiCoordinator().getWidth()
|
||||
);
|
||||
this.valid = resolution.isValid(description.screen, deviceScreen);
|
||||
|
||||
installActivity.setInstallEnabled(true);
|
||||
@@ -245,8 +252,6 @@ public class HuaweiInstallHandler implements InstallHandler {
|
||||
|
||||
LOG.debug("Initialized HuaweiInstallHandler: App");
|
||||
} else if (helper.isMusic()) {
|
||||
final HuaweiCoordinatorSupplier huaweiCoordinatorSupplier = (HuaweiCoordinatorSupplier) coordinator;
|
||||
|
||||
HuaweiMusicUtils.MusicCapabilities capabilities = huaweiCoordinatorSupplier.getHuaweiCoordinator().getExtendedMusicInfoParams();
|
||||
if (capabilities == null) {
|
||||
capabilities = huaweiCoordinatorSupplier.getHuaweiCoordinator().getMusicInfoParams();
|
||||
@@ -294,6 +299,12 @@ public class HuaweiInstallHandler implements InstallHandler {
|
||||
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Class<? extends Activity> getInstallActivity() {
|
||||
return FwAppInstallerActivity.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return helper.isValid();
|
||||
|
||||
+11
-5
@@ -17,6 +17,7 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.miband;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
|
||||
@@ -26,15 +27,16 @@ import org.slf4j.LoggerFactory;
|
||||
import java.io.IOException;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.FwAppInstallerActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.InstallActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.GenericItem;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
|
||||
|
||||
import static nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiFirmwareType.AGPS_UIHH;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiFirmwareType.WATCHFACE;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
public abstract class AbstractMiBandFWInstallHandler implements InstallHandler {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(AbstractMiBandFWInstallHandler.class);
|
||||
@@ -42,7 +44,6 @@ public abstract class AbstractMiBandFWInstallHandler implements InstallHandler {
|
||||
protected final Uri mUri;
|
||||
protected final Context mContext;
|
||||
protected AbstractMiBandFWHelper helper;
|
||||
private String errorMessage;
|
||||
|
||||
public AbstractMiBandFWInstallHandler(Uri uri, Context context) {
|
||||
mUri = uri;
|
||||
@@ -51,8 +52,7 @@ public abstract class AbstractMiBandFWInstallHandler implements InstallHandler {
|
||||
try {
|
||||
helper = createHelper(uri, context);
|
||||
} catch (IOException e) {
|
||||
errorMessage = e.getMessage();
|
||||
LOG.warn(errorMessage, e);
|
||||
LOG.error("Failed to open as miband fw", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,6 +151,12 @@ public abstract class AbstractMiBandFWInstallHandler implements InstallHandler {
|
||||
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Class<? extends Activity> getInstallActivity() {
|
||||
return FwAppInstallerActivity.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return helper != null;
|
||||
|
||||
+20
-20
@@ -17,9 +17,12 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.pebble;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.slf4j.Logger;
|
||||
@@ -34,6 +37,7 @@ import java.io.InputStream;
|
||||
import java.io.Writer;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.FwAppInstallerActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.InstallActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.appmanager.AppManagerActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
|
||||
@@ -119,16 +123,11 @@ public class PBWInstallHandler implements InstallHandler {
|
||||
if (mPBWReader.isLanguage()) {
|
||||
drawable = R.drawable.ic_languagepack;
|
||||
} else {
|
||||
switch (app.getType()) {
|
||||
case WATCHFACE:
|
||||
drawable = R.drawable.ic_watchface;
|
||||
break;
|
||||
case APP_ACTIVITYTRACKER:
|
||||
drawable = R.drawable.ic_activitytracker;
|
||||
break;
|
||||
default:
|
||||
drawable = R.drawable.ic_watchapp;
|
||||
}
|
||||
drawable = switch (app.getType()) {
|
||||
case WATCHFACE -> R.drawable.ic_watchface;
|
||||
case APP_ACTIVITYTRACKER -> R.drawable.ic_activitytracker;
|
||||
default -> R.drawable.ic_watchapp;
|
||||
};
|
||||
}
|
||||
installItem.setIcon(drawable);
|
||||
|
||||
@@ -155,12 +154,13 @@ public class PBWInstallHandler implements InstallHandler {
|
||||
GBDeviceApp app = mPBWReader.getGBDeviceApp();
|
||||
try {
|
||||
destDir = PebbleUtils.getPbwCacheDir();
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
destDir.mkdirs();
|
||||
FileUtils.copyURItoFile(mContext, mUri, new File(destDir, app.getUUID().toString() + ".pbw"));
|
||||
|
||||
AppManagerActivity.addToAppOrderFile("pbwcacheorder.txt", app.getUUID());
|
||||
} catch (IOException e) {
|
||||
LOG.error("Installation failed: " + e.getMessage(), e);
|
||||
LOG.error("Installation failed: ", e);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -169,7 +169,7 @@ public class PBWInstallHandler implements InstallHandler {
|
||||
try {
|
||||
writer = new BufferedWriter(new FileWriter(outputFile));
|
||||
} catch (IOException e) {
|
||||
LOG.error("Failed to open output file: " + e.getMessage(), e);
|
||||
LOG.error("Failed to open output file: ", e);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
@@ -183,26 +183,26 @@ public class PBWInstallHandler implements InstallHandler {
|
||||
|
||||
writer.close();
|
||||
} catch (IOException e) {
|
||||
LOG.error("Failed to write to output file: " + e.getMessage(), e);
|
||||
LOG.error("Failed to write to output file: ", e);
|
||||
} catch (JSONException e) {
|
||||
LOG.error(e.getMessage(), e);
|
||||
}
|
||||
|
||||
InputStream jsConfigFile = mPBWReader.getInputStreamFile("pebble-js-app.js");
|
||||
if (jsConfigFile != null) {
|
||||
try {
|
||||
try (jsConfigFile) {
|
||||
outputFile = new File(destDir, app.getUUID().toString() + "_config.js");
|
||||
FileUtils.copyStreamToFile(jsConfigFile, outputFile);
|
||||
} catch (IOException e) {
|
||||
LOG.error("Failed to open output file: " + e.getMessage(), e);
|
||||
} finally {
|
||||
try {
|
||||
jsConfigFile.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
LOG.error("Failed to open output file: ", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@NonNull
|
||||
@Override
|
||||
public Class<? extends Activity> getInstallActivity() {
|
||||
return FwAppInstallerActivity.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
|
||||
+11
-1
@@ -16,9 +16,12 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.pinetime;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
@@ -30,13 +33,14 @@ import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.FwAppInstallerActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.InstallActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.GenericItem;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.UriHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GBZipFile;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.UriHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.ZipFileException;
|
||||
|
||||
public class PineTimeInstallHandler implements InstallHandler {
|
||||
@@ -112,6 +116,12 @@ public class PineTimeInstallHandler implements InstallHandler {
|
||||
public void onStartInstall(GBDevice device) {
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Class<? extends Activity> getInstallActivity() {
|
||||
return FwAppInstallerActivity.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return dfuPackageManifest != null &&
|
||||
|
||||
+16
-7
@@ -17,11 +17,13 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.qhybrid;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||
|
||||
import org.json.JSONException;
|
||||
@@ -37,6 +39,7 @@ import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.FwAppInstallerActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.InstallActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.appmanager.AbstractAppManagerFragment;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
|
||||
@@ -45,7 +48,6 @@ import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceApp;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.GenericItem;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
|
||||
@@ -123,10 +125,11 @@ public class FossilHRInstallHandler implements InstallHandler {
|
||||
try {
|
||||
app = fossilFile.getGBDeviceApp();
|
||||
destDir = mCoordinator.getAppCacheDir();
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
destDir.mkdirs();
|
||||
FileUtils.copyURItoFile(mContext, fossilFile.getUri(), new File(destDir, app.getUUID().toString() + mCoordinator.getAppFileExtension()));
|
||||
} catch (IOException e) {
|
||||
LOG.error("Saving app in cache failed: " + e.getMessage(), e);
|
||||
LOG.error("Saving app in cache failed: ", e);
|
||||
return false;
|
||||
}
|
||||
// write app metadata
|
||||
@@ -135,7 +138,7 @@ public class FossilHRInstallHandler implements InstallHandler {
|
||||
try {
|
||||
writer = new BufferedWriter(new FileWriter(outputFile));
|
||||
} catch (IOException e) {
|
||||
LOG.error("Failed to open output file: " + e.getMessage(), e);
|
||||
LOG.error("Failed to open output file: ", e);
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
@@ -149,10 +152,10 @@ public class FossilHRInstallHandler implements InstallHandler {
|
||||
|
||||
writer.close();
|
||||
} catch (IOException e) {
|
||||
LOG.error("Failed to write to output file: " + e.getMessage(), e);
|
||||
LOG.error("Failed to write to output file: ", e);
|
||||
return false;
|
||||
} catch (JSONException e) {
|
||||
LOG.error("Failed to load or write appKeys JSON: " + e.getMessage(), e);
|
||||
LOG.error("Failed to load or write appKeys JSON: ", e);
|
||||
return false;
|
||||
}
|
||||
// write watchface background image
|
||||
@@ -163,7 +166,7 @@ public class FossilHRInstallHandler implements InstallHandler {
|
||||
backgroundImg.compress(Bitmap.CompressFormat.PNG, 9, fos);
|
||||
fos.close();
|
||||
} catch (IOException e) {
|
||||
LOG.error("Failed to write to output file: " + e.getMessage(), e);
|
||||
LOG.error("Failed to write to output file: ", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -175,13 +178,19 @@ public class FossilHRInstallHandler implements InstallHandler {
|
||||
previewImg.compress(Bitmap.CompressFormat.PNG, 9, fos);
|
||||
fos.close();
|
||||
} catch (IOException e) {
|
||||
LOG.error("Failed to write to output file: " + e.getMessage(), e);
|
||||
LOG.error("Failed to write to output file: ", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Class<? extends Activity> getInstallActivity() {
|
||||
return FwAppInstallerActivity.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return fossilFile.isValid();
|
||||
|
||||
+10
@@ -16,9 +16,12 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.qhybrid;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@@ -26,6 +29,7 @@ import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.FwAppInstallerActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.InstallActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
@@ -104,6 +108,12 @@ public class FossilInstallHandler implements InstallHandler {
|
||||
public void onStartInstall(GBDevice device) {
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Class<? extends Activity> getInstallActivity() {
|
||||
return FwAppInstallerActivity.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return mIsValid;
|
||||
|
||||
+7
-4
@@ -1,10 +1,13 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.thermalprinter;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -41,8 +44,10 @@ public class ImageFilePrinterHandler implements InstallHandler {
|
||||
|
||||
}
|
||||
|
||||
public Bitmap getIncomingBitmap() {
|
||||
return incomingBitmap;
|
||||
@NonNull
|
||||
@Override
|
||||
public Class<? extends Activity> getInstallActivity() {
|
||||
return SendToPrinterActivity.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -52,10 +57,8 @@ public class ImageFilePrinterHandler implements InstallHandler {
|
||||
|
||||
@Override
|
||||
public void validateInstallation(InstallActivity installActivity, GBDevice device) {
|
||||
|
||||
installActivity.setPreview(incomingBitmap);
|
||||
installActivity.setInstallEnabled(true);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+10
@@ -16,11 +16,15 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.xiaomi;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.FwAppInstallerActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.InstallActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
@@ -37,6 +41,12 @@ public class XiaomiInstallHandler implements InstallHandler {
|
||||
this.helper = new XiaomiFWHelper(uri, context);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Class<? extends Activity> getInstallActivity() {
|
||||
return FwAppInstallerActivity.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return helper.isValid();
|
||||
|
||||
+10
@@ -16,10 +16,14 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.cmfwatchpro;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.FwAppInstallerActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.InstallActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
@@ -36,6 +40,12 @@ public class CmfInstallHandler implements InstallHandler {
|
||||
this.helper = new CmfFwHelper(uri, context);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Class<? extends Activity> getInstallActivity() {
|
||||
return FwAppInstallerActivity.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return helper.isValid();
|
||||
|
||||
+11
@@ -18,10 +18,13 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos;
|
||||
|
||||
import static nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiFirmwareType.AGPS_UIHH;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.json.JSONObject;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -36,6 +39,7 @@ import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.FwAppInstallerActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.InstallActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
|
||||
@@ -59,6 +63,12 @@ public class ZeppOsFwInstallHandler implements InstallHandler {
|
||||
mHelper = new ZeppOsFwHelper(uri, context, deviceNames, deviceSources);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Class<? extends Activity> getInstallActivity() {
|
||||
return FwAppInstallerActivity.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return mHelper.isValid();
|
||||
@@ -149,6 +159,7 @@ public class ZeppOsFwInstallHandler implements InstallHandler {
|
||||
// write app zip
|
||||
final File appOutputFile = new File(appCacheDir, app.getUUID().toString() + coordinator.getAppFileExtension());
|
||||
try {
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
appCacheDir.mkdirs();
|
||||
FileUtils.copyURItoFile(mContext, mUri, appOutputFile);
|
||||
} catch (final IOException e) {
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="16dp"
|
||||
tools:context=".activities.install.FileInstallerActivity">
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/fileInstallerLoadingProgress"
|
||||
style="?android:attr/progressBarStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textInstallerStatus"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:textAppearance="?attr/textAppearanceListItem"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="@string/agps_status_error" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recyclerViewDevices"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:visibility="gone"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:listitem="@layout/list_item_device_install" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -15,8 +15,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingLeft="@dimen/about_margin"
|
||||
android:paddingRight="@dimen/about_margin"
|
||||
tools:context="nodomain.freeyourgadget.gadgetbridge.activities.OpenFwAppInstallerActivity">
|
||||
android:paddingRight="@dimen/about_margin">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:clickable="true"
|
||||
android:focusable="true">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="16dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageViewDeviceIcon"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:contentDescription="@string/icon_placeholder"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:srcCompat="@drawable/ic_device_unknown" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textViewDeviceName"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:textAppearance="?attr/textAppearanceListItem"
|
||||
android:textSize="18sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/imageViewDeviceIcon"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="@string/devicetype_unknown" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
@@ -137,6 +137,7 @@
|
||||
<!-- Strings related to CalBlacklist -->
|
||||
<string name="title_activity_calblacklist">Blacklisted Calendars</string>
|
||||
<!-- Strings related to FwAppInstaller -->
|
||||
<string name="title_activity_file_installer">File installer</string>
|
||||
<string name="title_activity_fw_app_insaller">FW/App installer</string>
|
||||
<string name="fw_upgrade_notice">You are about to install the %s.</string>
|
||||
<string name="fw_upgrade_notice_amazfitbip">You are about to install the %s firmware on your Amazfit Bip.\n\nPlease make sure to install the .fw file, then the .res file, and finally the .gps file. Your watch will reboot after installing the .fw file.\n\nNote: You do not have to install .res and .gps if these files are exactly the same as the ones previously installed.\n\nPROCEED AT YOUR OWN RISK!</string>
|
||||
@@ -1109,6 +1110,8 @@
|
||||
<string name="pref_title_keep_data_on_device">Keep activity data on device</string>
|
||||
<string name="miband_fwinstaller_incompatible_version">Incompatible firmware</string>
|
||||
<string name="fwinstaller_firmware_not_compatible_to_device">This firmware is not compatible with the device</string>
|
||||
<string name="fwinstaller_file_not_compatible_to_device">The selected file is not compatible with the device</string>
|
||||
<string name="fwinstaller_no_compatible_device_found">No compatible device found to install %1s</string>
|
||||
<string name="miband_prefs_reserve_alarm_calendar">Alarms to reserve for upcoming events</string>
|
||||
<string name="miband_prefs_reserve_reminder_calendar">Reminders to reserve for upcoming events</string>
|
||||
<string name="prefs_reserve_reminder_calendar_summary">Number of calendar events that will be synchronized</string>
|
||||
|
||||
Reference in New Issue
Block a user