Skip to content

Commit c13b07f

Browse files
committed
feat: create native iOS and Android WebView apps for PDF converter PWA
1 parent 8004c1f commit c13b07f

File tree

20 files changed

+1060
-0
lines changed

20 files changed

+1060
-0
lines changed

README-native-apps.md

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# PDF Converter Native Apps
2+
3+
This repository contains native iOS and Android applications that wrap the PDF Converter PWA (Progressive Web App) in a WebView. These apps provide a native experience for users while leveraging the existing web application.
4+
5+
## Project Structure
6+
7+
The repository is organized into two main directories:
8+
9+
- `/ios`: Contains the iOS application written in Swift
10+
- `/android`: Contains the Android application written in Kotlin
11+
12+
Each directory has its own README with specific instructions for building and running the respective applications.
13+
14+
## iOS App
15+
16+
The iOS app uses WKWebView to load the PWA from the production website. It provides a native iOS experience with the following features:
17+
18+
- Full support for PWA features through WKWebView
19+
- Progress indicator for page loading
20+
- Navigation controls
21+
- Error handling
22+
23+
See the [iOS README](./ios/README.md) for detailed instructions on building and running the iOS app.
24+
25+
## Android App
26+
27+
The Android app uses WebView to load the PWA from the production website. It provides a native Android experience with the following features:
28+
29+
- Full support for PWA features through WebView
30+
- Pull-to-refresh functionality
31+
- Back navigation handling
32+
- Error handling
33+
34+
See the [Android README](./android/README.md) for detailed instructions on building and running the Android app.
35+
36+
## Customization
37+
38+
Both apps can be easily customized to point to different environments (development, staging, production) by modifying the URL in the respective WebView implementation.
39+
40+
### iOS URL Configuration
41+
42+
In `ios/PDFConverter/PDFConverter/ViewController.swift`:
43+
44+
```swift
45+
private func loadWebsite() {
46+
if let url = URL(string: "https://profullstack.com/pdf") {
47+
let request = URLRequest(url: url)
48+
webView.load(request)
49+
}
50+
}
51+
```
52+
53+
### Android URL Configuration
54+
55+
In `android/PDFConverter/app/src/main/java/com/profullstack/pdfconverter/MainActivity.kt`:
56+
57+
```kotlin
58+
// URL of the PWA
59+
private val pwaUrl = "https://profullstack.com/pdf"
60+
```
61+
62+
## Benefits of Native WebView Apps
63+
64+
1. **App Store Presence**: Makes the application discoverable in app stores
65+
2. **Native Features**: Access to device capabilities not available to web apps
66+
3. **Offline Support**: Better offline capabilities through native caching
67+
4. **User Experience**: Provides a more integrated experience on mobile devices
68+
5. **Push Notifications**: Native push notification support
69+
6. **Icon on Home Screen**: Automatic placement on the user's home screen
70+
71+
## Future Enhancements
72+
73+
Potential future enhancements for these apps include:
74+
75+
1. Adding offline support with local caching
76+
2. Implementing push notifications
77+
3. Adding deep linking support
78+
4. Integrating native file pickers for better file handling
79+
5. Adding biometric authentication options
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
plugins {
2+
id 'com.android.application'
3+
id 'kotlin-android'
4+
}
5+
6+
android {
7+
compileSdkVersion 33
8+
9+
defaultConfig {
10+
applicationId "com.profullstack.pdfconverter"
11+
minSdkVersion 21
12+
targetSdkVersion 33
13+
versionCode 1
14+
versionName "1.0"
15+
16+
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
17+
}
18+
19+
buildTypes {
20+
release {
21+
minifyEnabled false
22+
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
23+
}
24+
}
25+
26+
compileOptions {
27+
sourceCompatibility JavaVersion.VERSION_1_8
28+
targetCompatibility JavaVersion.VERSION_1_8
29+
}
30+
31+
kotlinOptions {
32+
jvmTarget = '1.8'
33+
}
34+
35+
buildFeatures {
36+
viewBinding true
37+
}
38+
}
39+
40+
dependencies {
41+
implementation 'androidx.core:core-ktx:1.9.0'
42+
implementation 'androidx.appcompat:appcompat:1.6.1'
43+
implementation 'com.google.android.material:material:1.8.0'
44+
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
45+
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
46+
47+
// WebView related
48+
implementation 'androidx.webkit:webkit:1.6.0'
49+
50+
// Testing
51+
testImplementation 'junit:junit:4.13.2'
52+
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
53+
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
54+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
3+
package="com.profullstack.pdfconverter">
4+
5+
<!-- Internet permission for WebView -->
6+
<uses-permission android:name="android.permission.INTERNET" />
7+
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
8+
9+
<application
10+
android:allowBackup="true"
11+
android:icon="@mipmap/ic_launcher"
12+
android:label="@string/app_name"
13+
android:roundIcon="@mipmap/ic_launcher_round"
14+
android:supportsRtl="true"
15+
android:theme="@style/Theme.PDFConverter"
16+
android:usesCleartextTraffic="true">
17+
18+
<activity
19+
android:name=".MainActivity"
20+
android:exported="true"
21+
android:configChanges="orientation|keyboardHidden|screenSize"
22+
android:windowSoftInputMode="adjustResize">
23+
<intent-filter>
24+
<action android:name="android.intent.action.MAIN" />
25+
<category android:name="android.intent.category.LAUNCHER" />
26+
</intent-filter>
27+
</activity>
28+
</application>
29+
30+
</manifest>
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package com.profullstack.pdfconverter
2+
3+
import android.annotation.SuppressLint
4+
import android.os.Bundle
5+
import android.webkit.WebResourceError
6+
import android.webkit.WebResourceRequest
7+
import android.webkit.WebView
8+
import android.webkit.WebViewClient
9+
import android.widget.Toast
10+
import androidx.appcompat.app.AppCompatActivity
11+
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
12+
13+
class MainActivity : AppCompatActivity() {
14+
private lateinit var webView: WebView
15+
private lateinit var swipeRefreshLayout: SwipeRefreshLayout
16+
17+
// URL of the PWA
18+
private val pwaUrl = "https://profullstack.com/pdf"
19+
20+
@SuppressLint("SetJavaScriptEnabled")
21+
override fun onCreate(savedInstanceState: Bundle?) {
22+
super.onCreate(savedInstanceState)
23+
setContentView(R.layout.activity_main)
24+
25+
// Initialize WebView
26+
webView = findViewById(R.id.webView)
27+
swipeRefreshLayout = findViewById(R.id.swipeRefreshLayout)
28+
29+
// Configure WebView settings
30+
webView.settings.apply {
31+
javaScriptEnabled = true
32+
domStorageEnabled = true
33+
databaseEnabled = true
34+
useWideViewPort = true
35+
loadWithOverviewMode = true
36+
setSupportZoom(true)
37+
builtInZoomControls = true
38+
displayZoomControls = false
39+
// Enable PWA features
40+
setGeolocationEnabled(true)
41+
allowFileAccess = true
42+
allowContentAccess = true
43+
// Enable caching
44+
setAppCacheEnabled(true)
45+
}
46+
47+
// Set WebViewClient to handle page navigation
48+
webView.webViewClient = object : WebViewClient() {
49+
override fun onPageFinished(view: WebView?, url: String?) {
50+
super.onPageFinished(view, url)
51+
// Hide refresh indicator when page is loaded
52+
swipeRefreshLayout.isRefreshing = false
53+
}
54+
55+
override fun onReceivedError(view: WebView?, request: WebResourceRequest?, error: WebResourceError?) {
56+
super.onReceivedError(view, request, error)
57+
Toast.makeText(this@MainActivity, "Error loading page", Toast.LENGTH_SHORT).show()
58+
swipeRefreshLayout.isRefreshing = false
59+
}
60+
61+
// Keep navigation within the WebView
62+
override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
63+
return false
64+
}
65+
}
66+
67+
// Set up swipe to refresh
68+
swipeRefreshLayout.setOnRefreshListener {
69+
webView.reload()
70+
}
71+
72+
// Load the PWA
73+
webView.loadUrl(pwaUrl)
74+
}
75+
76+
// Handle back button to navigate within WebView history
77+
override fun onBackPressed() {
78+
if (webView.canGoBack()) {
79+
webView.goBack()
80+
} else {
81+
super.onBackPressed()
82+
}
83+
}
84+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
3+
xmlns:android="http://schemas.android.com/apk/res/android"
4+
xmlns:app="http://schemas.android.com/apk/res-auto"
5+
xmlns:tools="http://schemas.android.com/tools"
6+
android:id="@+id/swipeRefreshLayout"
7+
android:layout_width="match_parent"
8+
android:layout_height="match_parent"
9+
tools:context=".MainActivity">
10+
11+
<WebView
12+
android:id="@+id/webView"
13+
android:layout_width="match_parent"
14+
android:layout_height="match_parent" />
15+
16+
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<resources xmlns:tools="http://schemas.android.com/tools">
2+
<!-- Base application theme. -->
3+
<style name="Theme.PDFConverter" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
4+
<!-- Primary brand color. -->
5+
<item name="colorPrimary">@color/purple_200</item>
6+
<item name="colorPrimaryVariant">@color/purple_700</item>
7+
<item name="colorOnPrimary">@color/black</item>
8+
<!-- Secondary brand color. -->
9+
<item name="colorSecondary">@color/teal_200</item>
10+
<item name="colorSecondaryVariant">@color/teal_200</item>
11+
<item name="colorOnSecondary">@color/black</item>
12+
<!-- Status bar color. -->
13+
<item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
14+
<!-- Customize your theme here. -->
15+
</style>
16+
</resources>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<resources>
3+
<color name="purple_200">#FFBB86FC</color>
4+
<color name="purple_500">#FF6200EE</color>
5+
<color name="purple_700">#FF3700B3</color>
6+
<color name="teal_200">#FF03DAC5</color>
7+
<color name="teal_700">#FF018786</color>
8+
<color name="black">#FF000000</color>
9+
<color name="white">#FFFFFFFF</color>
10+
</resources>
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<resources>
2+
<string name="app_name">PDF Converter</string>
3+
<string name="loading">Loading...</string>
4+
<string name="error_loading">Error loading page</string>
5+
<string name="retry">Retry</string>
6+
</resources>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<resources xmlns:tools="http://schemas.android.com/tools">
2+
<!-- Base application theme. -->
3+
<style name="Theme.PDFConverter" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
4+
<!-- Primary brand color. -->
5+
<item name="colorPrimary">@color/purple_500</item>
6+
<item name="colorPrimaryVariant">@color/purple_700</item>
7+
<item name="colorOnPrimary">@color/white</item>
8+
<!-- Secondary brand color. -->
9+
<item name="colorSecondary">@color/teal_200</item>
10+
<item name="colorSecondaryVariant">@color/teal_700</item>
11+
<item name="colorOnSecondary">@color/black</item>
12+
<!-- Status bar color. -->
13+
<item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
14+
<!-- Customize your theme here. -->
15+
</style>
16+
</resources>

android/PDFConverter/build.gradle

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Top-level build file where you can add configuration options common to all sub-projects/modules.
2+
buildscript {
3+
repositories {
4+
google()
5+
mavenCentral()
6+
}
7+
dependencies {
8+
classpath 'com.android.tools.build:gradle:7.4.2'
9+
classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.0'
10+
11+
// NOTE: Do not place your application dependencies here; they belong
12+
// in the individual module build.gradle files
13+
}
14+
}
15+
16+
allprojects {
17+
repositories {
18+
google()
19+
mavenCentral()
20+
}
21+
}
22+
23+
task clean(type: Delete) {
24+
delete rootProject.buildDir
25+
}

0 commit comments

Comments
 (0)