Printer: Use onInstallApp

This commit is contained in:
José Rebelo
2025-07-20 13:51:49 +01:00
parent d2612ff8cb
commit c38d00f7a3
4 changed files with 51 additions and 96 deletions
@@ -8,14 +8,13 @@ import android.net.Uri;
import android.os.Bundle;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import com.google.android.material.materialswitch.MaterialSwitch;
@@ -26,7 +25,6 @@ import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -36,6 +34,7 @@ import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBActivity;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.service.devices.thermalprinter.BitmapToBitSet;
import nodomain.freeyourgadget.gadgetbridge.service.devices.thermalprinter.GenericThermalPrinterSupport;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.UriHelper;
@@ -48,6 +47,8 @@ public class SendToPrinterActivity extends AbstractGBActivity {
private ImageView previewImage;
private MaterialSwitch dithering;
private MaterialSwitch upscale;
private GBDevice device;
private final GestureDetector.SimpleOnGestureListener gestureListener = new GestureDetector.SimpleOnGestureListener() {
private static final int SWIPE_THRESHOLD = 100;
@@ -75,11 +76,13 @@ public class SendToPrinterActivity extends AbstractGBActivity {
}
};
private File printPicture = null;
private Uri uri;
ActivityResultLauncher<Intent> pickImageLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
result -> {
if (result.getResultCode() == Activity.RESULT_OK) {
Uri uri = result.getData().getData();
uri = result.getData().getData();
processUriAsync(uri);
}
}
@@ -97,40 +100,23 @@ public class SendToPrinterActivity extends AbstractGBActivity {
final TextView warning = findViewById(R.id.warning_devices);
upscale = findViewById(R.id.switchUpscale);
final List<GBDevice> devices = ((GBApplication) getApplicationContext()).getDeviceManager().getSelectedDevices();
GBDevice device = devices.get(0);
switch (devices.size()) {
case 0:
warning.setText(R.string.open_fw_installer_connect_minimum_one_device);
sendToPrinter.setEnabled(false);
break;
case 1:
warning.setText(String.format(getString(R.string.open_fw_installer_select_file), device.getAliasOrName()));
sendToPrinter.setEnabled(true);
break;
default:
warning.setText(R.string.open_fw_installer_connect_maximum_one_device);
sendToPrinter.setEnabled(false);
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 SendToPrinterActivity", Toast.LENGTH_LONG, GB.ERROR);
finish();
return;
}
gestureDetector = new GestureDetector(this, gestureListener);
previewImage.setOnTouchListener((v, event) -> gestureDetector.onTouchEvent(event));
dithering.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
LOG.info("dithering is : {}", dithering.isChecked());
updatePreview();
}
dithering.setOnClickListener(view -> {
LOG.info("dithering is : {}", dithering.isChecked());
updatePreview();
});
sendToPrinter.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
sendToPrinter();
}
});
sendToPrinter.setOnClickListener(v -> sendToPrinter(device));
upscale.setOnCheckedChangeListener((buttonView, isChecked) -> {
if (isChecked) {
@@ -144,7 +130,10 @@ public class SendToPrinterActivity extends AbstractGBActivity {
});
printPicture = new File(getCacheDir(), "temp_bitmap.png");
final Uri uri = getIntent().getParcelableExtra(GenericThermalPrinterSupport.INTENT_EXTRA_URI);
uri = getIntent().getData();
if (uri == null) { // For "share" intent
uri = getIntent().getParcelableExtra(Intent.EXTRA_STREAM);
}
if (uri != null) {
processUriAsync(uri);
} else {
@@ -207,13 +196,13 @@ public class SendToPrinterActivity extends AbstractGBActivity {
previewImage.setImageBitmap(bitmapToBitSet.getPreview());
}
private void sendToPrinter() {
Intent intent = new Intent(GenericThermalPrinterSupport.INTENT_ACTION_PRINT_BITMAP);
intent.putExtra(GenericThermalPrinterSupport.INTENT_EXTRA_BITMAP_CACHE_FILE_PATH, printPicture.getAbsolutePath());
intent.putExtra(GenericThermalPrinterSupport.INTENT_EXTRA_APPLY_DITHERING, dithering.isChecked());
intent.putExtra(GenericThermalPrinterSupport.INTENT_EXTRA_UPSCALE, upscale.isChecked());
intent.putExtra(GenericThermalPrinterSupport.INTENT_EXTRA_ALIGNMENT, alignment);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
private void sendToPrinter(final GBDevice device) {
final Bundle options = new Bundle();
options.putString(GenericThermalPrinterSupport.INTENT_EXTRA_BITMAP_CACHE_FILE_PATH, printPicture.getAbsolutePath());
options.putBoolean(GenericThermalPrinterSupport.INTENT_EXTRA_APPLY_DITHERING, dithering.isChecked());
options.putBoolean(GenericThermalPrinterSupport.INTENT_EXTRA_UPSCALE, upscale.isChecked());
options.putSerializable(GenericThermalPrinterSupport.INTENT_EXTRA_ALIGNMENT, alignment);
GBApplication.deviceService(device).onInstallApp(uri, options);
}
private void cleanUpPrintPictureCache() {
@@ -72,17 +72,13 @@ public class ThermalPrinterCoordinator extends AbstractBLEDeviceCoordinator {
@Nullable
@Override
public InstallHandler findInstallHandler(Uri uri, Context context) {
//TODO: maybe there is another way to implement opening/receiving pictures
final ImageFilePrinterHandler imageFilePrinterHandler = new ImageFilePrinterHandler(uri, context);
if (imageFilePrinterHandler.isValid()) {
Intent instentStartPrintActivity = new Intent(context, SendToPrinterActivity.class);
instentStartPrintActivity.putExtra(GenericThermalPrinterSupport.INTENT_EXTRA_URI, uri);
context.startActivity(instentStartPrintActivity);
return imageFilePrinterHandler;
}
return null;
}
@Override
public List<DeviceCardAction> getCustomActions() {
return Collections.singletonList(new ControlDeviceCardAction());
@@ -58,6 +58,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
@@ -1097,10 +1098,12 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
}
case ACTION_INSTALL:
Uri uri = intentCopy.getParcelableExtra(EXTRA_URI);
Bundle options = intentCopy.getBundleExtra(EXTRA_OPTIONS);
Bundle options = Objects.requireNonNullElse(intentCopy.getBundleExtra(EXTRA_OPTIONS), Bundle.EMPTY);
if (uri != null) {
LOG.info("will try to install app/fw");
deviceSupport.onInstallApp(uri, options);
} else {
LOG.error("Got null uri for app to install");
}
break;
case ACTION_SET_ALARMS:
@@ -2,10 +2,6 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.thermalprinter;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
@@ -22,7 +18,6 @@ import android.text.TextPaint;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -45,8 +40,6 @@ import nodomain.freeyourgadget.gadgetbridge.util.GB;
public class GenericThermalPrinterSupport extends AbstractBTLESingleDeviceSupport {
public static final int IMAGE_WIDTH = 384;
public static final String INTENT_ACTION_PRINT_BITMAP = "print_bitmap";
public static final String INTENT_EXTRA_URI = "picture_uri";
public static final String INTENT_EXTRA_BITMAP_CACHE_FILE_PATH = "bitmap_cache_file_path";
public static final String INTENT_EXTRA_APPLY_DITHERING = "apply_dithering";
public static final String INTENT_EXTRA_UPSCALE = "upscale";
@@ -65,32 +58,6 @@ public class GenericThermalPrinterSupport extends AbstractBTLESingleDeviceSuppor
private final int PRINT_TYPE = 0; //1 also observed
private boolean useRunLengthEncoding = false;
private boolean canPrint = false;
private final BroadcastReceiver printCommandReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
switch (intent.getAction()) {
case INTENT_ACTION_PRINT_BITMAP:
String picturePath = intent.getStringExtra(INTENT_EXTRA_BITMAP_CACHE_FILE_PATH);
if (picturePath.isEmpty()) {
LOG.error("Cannot print: picturePath is empty");
return;
}
final Bitmap incoming = BitmapFactory.decodeFile(picturePath);
final Bitmap bitmap = intent.getBooleanExtra(INTENT_EXTRA_UPSCALE, false) ?
Bitmap.createScaledBitmap(incoming, IMAGE_WIDTH, (int) (IMAGE_WIDTH * (float) incoming.getHeight() / incoming.getWidth()), true)
: incoming;
if (bitmap == null) {
LOG.error("Cannot print: bitmap is null");
return;
}
boolean dither = intent.getBooleanExtra(INTENT_EXTRA_APPLY_DITHERING, false);
PrintAlignment alignment = (PrintAlignment) intent.getSerializableExtra(INTENT_EXTRA_ALIGNMENT);
printImage(bitmap, dither, alignment);
}
}
};
public GenericThermalPrinterSupport() {
super(LOG);
@@ -374,19 +341,11 @@ public class GenericThermalPrinterSupport extends AbstractBTLESingleDeviceSuppor
builder.setDeviceState(GBDevice.State.INITIALIZED);
registerPrintCommandReceiver();
LOG.debug("Connected to: " + gbDevice.getName());
LOG.debug("Connected to: {}", gbDevice.getName());
return builder;
}
private void registerPrintCommandReceiver() {
IntentFilter commandFilter = new IntentFilter();
commandFilter.addAction(INTENT_ACTION_PRINT_BITMAP);
LocalBroadcastManager.getInstance(GBApplication.getContext()).registerReceiver(printCommandReceiver, commandFilter);
}
public void send(String taskname, byte[] command) {
TransactionBuilder builder = createTransactionBuilder(taskname);
builder.writeChunkedData(getCharacteristic(writeCharUUID), command, getMTU() - 3);
@@ -400,14 +359,6 @@ public class GenericThermalPrinterSupport extends AbstractBTLESingleDeviceSuppor
sendTestPrint();
}
@Override
public void dispose() {
synchronized (ConnectionMonitor) {
super.dispose();
LocalBroadcastManager.getInstance(getContext()).unregisterReceiver(printCommandReceiver);
}
}
@Override
public void onSendConfiguration(String config) {
switch (config) {
@@ -441,7 +392,23 @@ public class GenericThermalPrinterSupport extends AbstractBTLESingleDeviceSuppor
@Override
public void onInstallApp(Uri uri, @NonNull final Bundle options) {
//we just abuse the install functionality to start our own activity
String picturePath = options.getString(INTENT_EXTRA_BITMAP_CACHE_FILE_PATH, "");
if (picturePath.isEmpty()) {
LOG.error("Cannot print: picturePath is empty");
return;
}
final Bitmap incoming = BitmapFactory.decodeFile(picturePath);
final Bitmap bitmap = options.getBoolean(INTENT_EXTRA_UPSCALE, false) ?
Bitmap.createScaledBitmap(incoming, IMAGE_WIDTH, (int) (IMAGE_WIDTH * (float) incoming.getHeight() / incoming.getWidth()), true)
: incoming;
if (bitmap == null) {
LOG.error("Cannot print: bitmap is null");
return;
}
boolean dither = options.getBoolean(INTENT_EXTRA_APPLY_DITHERING, false);
PrintAlignment alignment = (PrintAlignment) options.getSerializable(INTENT_EXTRA_ALIGNMENT);
printImage(bitmap, dither, alignment);
}
public enum PrintAlignment {