feat: implementar descarga de PDF de factura y soporte para guardar en Android
This commit is contained in:
+54
-7
@@ -1,11 +1,17 @@
|
|||||||
package com.hdrdevs.turnosxpress;
|
package com.hdrdevs.turnosxpress;
|
||||||
|
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.core.app.ActivityCompat;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
import android.content.ActivityNotFoundException;
|
import android.content.ActivityNotFoundException;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.media.MediaScannerConnection;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
@@ -33,6 +39,7 @@ import android.content.ContentResolver;
|
|||||||
public class MainActivity extends AppCompatActivity {
|
public class MainActivity extends AppCompatActivity {
|
||||||
|
|
||||||
private static final int LOGIN_REQUEST_CODE = 1;
|
private static final int LOGIN_REQUEST_CODE = 1;
|
||||||
|
private static final int STORAGE_PERMISSION_REQUEST_CODE = 2;
|
||||||
private WebView webView;
|
private WebView webView;
|
||||||
private ValueCallback<Uri[]> fileChooserCallback;
|
private ValueCallback<Uri[]> fileChooserCallback;
|
||||||
|
|
||||||
@@ -43,6 +50,7 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
tokenResult = "";
|
tokenResult = "";
|
||||||
|
requestLegacyStoragePermissionIfNeeded();
|
||||||
|
|
||||||
// For Android Emulator, use 10.0.2.2 to access localhost on your development machine.
|
// For Android Emulator, use 10.0.2.2 to access localhost on your development machine.
|
||||||
// If testing on a physical device, replace 10.0.2.2 with your development machine's actual local IP address.
|
// If testing on a physical device, replace 10.0.2.2 with your development machine's actual local IP address.
|
||||||
@@ -220,6 +228,28 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void requestLegacyStoragePermissionIfNeeded() {
|
||||||
|
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P
|
||||||
|
&& ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
|
||||||
|
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, STORAGE_PERMISSION_REQUEST_CODE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String safePdfFilename(String filename) {
|
||||||
|
String safeName = filename == null ? "reporte.pdf" : filename.trim();
|
||||||
|
safeName = safeName.replaceAll("[\\\\/:*?\"<>|]", "_");
|
||||||
|
|
||||||
|
if (safeName.length() == 0) {
|
||||||
|
safeName = "reporte.pdf";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!safeName.toLowerCase().endsWith(".pdf")) {
|
||||||
|
safeName += ".pdf";
|
||||||
|
}
|
||||||
|
|
||||||
|
return safeName;
|
||||||
|
}
|
||||||
|
|
||||||
public class WebAppInterface {
|
public class WebAppInterface {
|
||||||
@JavascriptInterface
|
@JavascriptInterface
|
||||||
public void startGoogleLogin() {
|
public void startGoogleLogin() {
|
||||||
@@ -235,16 +265,20 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
@JavascriptInterface
|
@JavascriptInterface
|
||||||
public void savePdf(String base64Data, String filename) {
|
public void savePdf(String base64Data, String filename) {
|
||||||
|
String safeFilename = safePdfFilename(filename);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
byte[] pdfAsBytes = Base64.decode(base64Data, 0);
|
String normalizedBase64 = base64Data == null ? "" : base64Data.replaceFirst("^data:application/pdf;base64,", "");
|
||||||
|
byte[] pdfAsBytes = Base64.decode(normalizedBase64, Base64.DEFAULT);
|
||||||
boolean isSaved = false;
|
boolean isSaved = false;
|
||||||
|
|
||||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
ContentResolver resolver = getContentResolver();
|
ContentResolver resolver = getContentResolver();
|
||||||
ContentValues contentValues = new ContentValues();
|
ContentValues contentValues = new ContentValues();
|
||||||
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, filename);
|
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, safeFilename);
|
||||||
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "application/pdf");
|
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "application/pdf");
|
||||||
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS);
|
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS);
|
||||||
|
contentValues.put(MediaStore.MediaColumns.IS_PENDING, 1);
|
||||||
|
|
||||||
Uri uri = resolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues);
|
Uri uri = resolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues);
|
||||||
|
|
||||||
@@ -253,18 +287,31 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
outputStream.write(pdfAsBytes);
|
outputStream.write(pdfAsBytes);
|
||||||
isSaved = true;
|
isSaved = true;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
Log.e("WEBVIEW", "Error writing PDF to MediaStore: " + safeFilename, e);
|
||||||
|
resolver.delete(uri, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isSaved) {
|
||||||
|
ContentValues completedValues = new ContentValues();
|
||||||
|
completedValues.put(MediaStore.MediaColumns.IS_PENDING, 0);
|
||||||
|
resolver.update(uri, completedValues, null, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
|
File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
|
||||||
File file = new File(path, filename);
|
|
||||||
|
if (!path.exists() && !path.mkdirs()) {
|
||||||
|
Log.e("WEBVIEW", "Could not create Downloads directory: " + path.getAbsolutePath());
|
||||||
|
} else {
|
||||||
|
File file = new File(path, safeFilename);
|
||||||
|
|
||||||
try (FileOutputStream os = new FileOutputStream(file)) {
|
try (FileOutputStream os = new FileOutputStream(file)) {
|
||||||
os.write(pdfAsBytes);
|
os.write(pdfAsBytes);
|
||||||
isSaved = true;
|
isSaved = true;
|
||||||
|
MediaScannerConnection.scanFile(MainActivity.this, new String[]{file.getAbsolutePath()}, new String[]{"application/pdf"}, null);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
Log.e("WEBVIEW", "Error writing PDF to Downloads: " + safeFilename, e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -275,7 +322,7 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
Log.e("WEBVIEW", "Error saving PDF: " + safeFilename, e);
|
||||||
runOnUiThread(() -> Toast.makeText(MainActivity.this, "Error: " + e.getMessage(), Toast.LENGTH_LONG).show());
|
runOnUiThread(() -> Toast.makeText(MainActivity.this, "Error: " + e.getMessage(), Toast.LENGTH_LONG).show());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-1
@@ -844,7 +844,16 @@ export default function CashFlowMovementsList() {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
(pdfMake as any).createPdf({ ...documentDefinition, content }).download(`factura-c-${invoice.pointOfSale || fiscalProfile.pointOfSale || 0}-${invoice.voucherNumber || 0}.pdf`);
|
const filename = `factura-c-${invoice.pointOfSale || fiscalProfile.pointOfSale || 0}-${invoice.voucherNumber || 0}.pdf`;
|
||||||
|
const pdf = (pdfMake as any).createPdf({ ...documentDefinition, content });
|
||||||
|
|
||||||
|
if ((window as unknown as { Android: any }).Android && (window as unknown as { Android: any }).Android.savePdf) {
|
||||||
|
pdf.getBase64((data: string) => {
|
||||||
|
(window as unknown as { Android: any }).Android?.savePdf(data, filename);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
pdf.download(filename);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error generating invoice PDF:", error);
|
console.error("Error generating invoice PDF:", error);
|
||||||
alert.showError("No pudimos generar el PDF de la factura.");
|
alert.showError("No pudimos generar el PDF de la factura.");
|
||||||
|
|||||||
Reference in New Issue
Block a user