Summary PDF of a Webpage from Android App
In this video it shows the steps and code to print the summary of a webpage in a pdf file. It gets the summary of the webpage using an LLM model from Hugging Face.
In this video it refers the code from the below: https://programmerworld.co/android/create-ai-voice-assistant-using-huggingface-llm-models-apis-to-summarize-a-webpage-in-android-app/
I hope you like this video. For any questions, suggestions or appreciation please contact us at: https://programmerworld.co/contact/ or email at: programmerworld1990@gmail.com
Details:
Java Code:
package com.programmerworld.summarydoc;
//How to generate a PDF report with summary of any Webpage from your Android App?
import android.content.ContentResolver;
import android.content.ContentValues;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.pdf.PdfDocument;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.provider.MediaStore;
import android.speech.tts.TextToSpeech;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import okhttp3.*;
public class MainActivity extends AppCompatActivity {
private EditText urlInput;
private Button summarizeButton, savePdfButton;
private TextView resultText;
private final String API_URL = "https://api-inference.huggingface.co/models/facebook/bart-large-cnn";
private final String API_KEY = "hf_BNhDUMMYDUMPIcSGPCPwlWUlyGDudkUg"; // Replace with your actual API Key
private final ExecutorService executorService = Executors.newSingleThreadExecutor();
private final Handler uiHandler = new Handler(Looper.getMainLooper());
private TextToSpeech textToSpeech;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
urlInput = findViewById(R.id.urlInput);
summarizeButton = findViewById(R.id.summarizeButton);
savePdfButton = findViewById(R.id.savePdfButton);
resultText = findViewById(R.id.resultText);
summarizeButton.setOnClickListener(view -> {
String url = urlInput.getText().toString();
fetchWebContent(url);
});
savePdfButton.setOnClickListener(view -> {
saveSummaryAsPdf();
});
// Initialize Text-to-Speech
textToSpeech = new TextToSpeech(getApplicationContext(), status -> {
if (status == TextToSpeech.SUCCESS) {
textToSpeech.setLanguage(Locale.ENGLISH);
}
});
}
// Fetch webpage content using Jsoup
private void fetchWebContent(String url) {
executorService.execute(() -> {
try {
Document doc = Jsoup.connect(url).get();
String extractedText = doc.text(); // Extract text from the webpage
// Limit the extracted text to avoid API limits
if (extractedText.length() > 1024) {
extractedText = extractedText.substring(0, 1024);
}
summarizeText(extractedText);
} catch (IOException e) {
String errorMsg = "Error fetching webpage: " + e.getMessage();
uiHandler.post(() -> resultText.setText(errorMsg));
}
});
}
// Send extracted text to Hugging Face API for summarization
private void summarizeText(String inputText) {
executorService.execute(() -> {
try {
OkHttpClient client = new OkHttpClient();
String json = "{"
+ "\"inputs\": \"" + inputText.replace("\"", "\\\"") + "\","
+ "\"parameters\": {"
+ "\"max_length\": 200,"
+ "\"min_length\": 50,"
+ "\"do_sample\": false"
+ "}"
+ "}";
RequestBody body = RequestBody.create(json, MediaType.get("application/json"));
Request request = new Request.Builder()
.url(API_URL)
.addHeader("Authorization", "Bearer " + API_KEY)
.post(body)
.build();
Response response = client.newCall(request).execute();
String responseBody = response.body() != null ? response.body().string() : "Error: Empty response";
// Update UI on main thread
uiHandler.post(() -> resultText.setText(responseBody));
readOutSummary(responseBody);
} catch (IOException e) {
uiHandler.post(() -> resultText.setText("Error calling API: " + e.getMessage()));
}
});
}
// Method to read out the summary using TTS
private void readOutSummary(String textToRead) {
if (!textToRead.isEmpty()) {
textToSpeech.speak(textToRead, TextToSpeech.QUEUE_FLUSH, null, null);
}
}
private void saveSummaryAsPdf() {
String summaryText = resultText.getText().toString();
if (summaryText.isEmpty()) {
Toast.makeText(this, "No summary to save!", Toast.LENGTH_SHORT).show();
return;
}
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.Downloads.DISPLAY_NAME, "Summary.pdf");
contentValues.put(MediaStore.Downloads.MIME_TYPE, "application/pdf");
ContentResolver contentResolver = getContentResolver();
Uri pdfUri = contentResolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues);
if (pdfUri == null) {
Toast.makeText(this, "Failed to create PDF", Toast.LENGTH_SHORT).show();
return;
}
try (OutputStream outputStream = contentResolver.openOutputStream(pdfUri)) {
PdfDocument pdfDocument = new PdfDocument();
PdfDocument.PageInfo pageInfo = new PdfDocument.PageInfo.Builder(600, 800, 1).create();
PdfDocument.Page page = pdfDocument.startPage(pageInfo);
Canvas canvas = page.getCanvas();
Paint paint = new Paint();
paint.setTextSize(16);
paint.setColor(Color.BLACK);
int x = 50; // Left margin
int y = 50; // Top margin
int lineHeight = 25; // Space between lines
int maxWidth = pageInfo.getPageWidth() - (2 * x); // Page width with margins
// Manually wrap text to fit within maxWidth
for (String line : wrapText(summaryText, paint, maxWidth)) {
canvas.drawText(line, x, y, paint);
y += lineHeight;
}
pdfDocument.finishPage(page);
pdfDocument.writeTo(outputStream);
pdfDocument.close();
Toast.makeText(this, "PDF saved in Downloads!", Toast.LENGTH_LONG).show();
} catch (IOException e) {
Toast.makeText(this, "Error saving PDF: " + e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
private List<String> wrapText(String text, Paint paint, int maxWidth) {
List<String> lines = new ArrayList<>();
String[] words = text.split(" ");
StringBuilder currentLine = new StringBuilder();
for (String word : words) {
if (paint.measureText(currentLine + " " + word) <= maxWidth) {
currentLine.append(" ").append(word);
} else {
lines.add(currentLine.toString().trim());
currentLine = new StringBuilder(word);
}
}
lines.add(currentLine.toString().trim()); // Add the last line
return lines;
}
@Override
protected void onDestroy() {
if (textToSpeech != null) {
textToSpeech.stop();
textToSpeech.shutdown();
}
super.onDestroy();
}
}
plugins {
alias(libs.plugins.android.application)
}
android {
namespace = "com.programmerworld.summarydoc"
compileSdk = 35
defaultConfig {
applicationId = "com.programmerworld.summarydoc"
minSdk = 30
targetSdk = 35
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
}
dependencies {
implementation(libs.appcompat)
implementation(libs.material)
implementation(libs.activity)
implementation(libs.constraintlayout)
testImplementation(libs.junit)
androidTestImplementation(libs.ext.junit)
androidTestImplementation(libs.espresso.core)
implementation("org.jsoup:jsoup:1.15.3") // For web scraping
implementation("com.squareup.okhttp3:okhttp:4.9.3") // For HTTP requests
}
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.SummaryDoc"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">
<EditText
android:id="@+id/urlInput"
android:layout_width="383dp"
android:layout_height="82dp"
android:hint="Enter webpage URL"
android:inputType="textUri" />
<Button
android:id="@+id/summarizeButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Summarize" />
<Button
android:id="@+id/savePdfButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Save as PDF" />
<TextView
android:id="@+id/resultText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="16dp"
android:textSize="20sp" />
</LinearLayout>
Screenshots:
Initial Layout:
Summary generated:
File generated:
PDF Output: