Android WebView App

Android App Development
Complete Guide

Step-by-step টিউটোরিয়াল — আপনার টিম সহজেই কপি করে অ্যাপ বানাতে পারবে।

৪টি মডিউল সম্পূর্ণ সোর্স কোড Java + Android Studio Firebase Notification
MODULE 1 — Basic Android WebView App
STEP 01
New Project তৈরি করুন
  • 1File → New → New Project এ ক্লিক করুন
  • 2Empty View Activity সিলেক্ট করুন
  • 3প্রজেক্টের নাম, Package name ও Save location দিন
  • 4Finish বাটন ক্লিক করুন
STEP 02
activity_main.xml — WebView যোগ করুন

TextView সরিয়ে নিচের WebView কোড paste করুন:

activity_main.xml
<WebView
   android:id="@+id/WebviewID"
   android:layout_width="match_parent"
   android:layout_height="match_parent"/>
STEP 03
MainActivity.java — WebView Variable
public class MainActivity extends AppCompatActivity { এই লাইনের পরে paste করুন। লাল রঙ দেখালে Alt + Enter চাপুন।
MainActivity.java — Variables
WebView webView;
ProgressDialog progressDialog;
STEP 04
MainActivity.java — WebView Setup
setContentView(R.layout.activity_main); এর পরে paste করুন
MainActivity.java — onCreate()
webView = (WebView) findViewById(R.id.WebviewID);
webView.loadUrl("https://yourwebsite.com/"); // ← আপনার URL দিন

WebSettings webSettings = webView.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setSupportZoom(true);
webSettings.setBuiltInZoomControls(false);
webSettings.setCacheMode(WebSettings.LOAD_NO_CACHE);
webSettings.setDomStorageEnabled(true);

webView.setScrollBarStyle(WebView.SCROLLBARS_OUTSIDE_OVERLAY);
webView.setScrollbarFadingEnabled(true);

progressDialog = new ProgressDialog(this);
progressDialog.setMessage("Loading Please Wait...");

webView.setWebViewClient(new WebViewClient());
webView.setWebChromeClient(new WebChromeClient() {
    @Override
    public void onProgressChanged(WebView view, int newProgress) {
        progressDialog.show();
        if (newProgress == 100) {
            progressDialog.dismiss();
        }
        super.onProgressChanged(view, newProgress);
    }
});

// Back Button
@Override
public void onBackPressed() {
    if (webView.canGoBack()) {
        webView.goBack();
    } else {
        super.onBackPressed();
    }
}
STEP 05
AndroidManifest.xml — Internet Permission
<application ট্যাগের আগে paste করুন
AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET"/>
STEP 06-08
Logo, Theme ও Sign APK
  • 6Logo Change: Logo copy করে res → Drawable ফোল্ডারে paste করুন। Manifest এ logo সিলেক্ট করুন।
  • 7Theme: res → values → themes.xml এ দুটো জায়গায় DarkActionBar থেকে NoActionBar করুন।
  • 8Build APK: Menu → Build → Generate Signed App
MODULE 2 — File Upload + Camera
STEP 01
activity_main.xml — WebView Layout
activity_main.xml
<androidx.constraintlayout.widget.ConstraintLayout
   xmlns:android="http://schemas.android.com/apk/res/android"
   android:id="@+id/main"
   android:layout_width="match_parent"
   android:layout_height="match_parent">

   <WebView
       android:id="@+id/WebviewID"
       android:layout_width="match_parent"
       android:layout_height="match_parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>
STEP 02
MainActivity.java — File Upload Full Code (Android 13+)
com.imanteams.demando_restaurant_partner.provider — এটি আপনার App ID দিয়ে পরিবর্তন করুন
MainActivity.java — Full Code
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Toast;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;
import androidx.core.content.FileProvider;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class MainActivity extends AppCompatActivity {

   WebView webView;
   ValueCallback<Uri[]> filePathCallback;
   Uri cameraImageUri;
   ActivityResultLauncher<Intent> fileChooserLauncher;

   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);

       requestCameraPermission();
       webView = findViewById(R.id.WebviewID);
       webView.loadUrl("https://yourwebsite.com/"); // ← URL পরিবর্তন করুন

       WebSettings settings = webView.getSettings();
       settings.setJavaScriptEnabled(true);
       settings.setDomStorageEnabled(true);
       settings.setAllowFileAccess(true);
       settings.setAllowContentAccess(true);
       settings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);

       fileChooserLauncher = registerForActivityResult(
           new ActivityResultContracts.StartActivityForResult(),
           result -> {
               Uri[] results = null;
               if (result.getResultCode() == RESULT_OK) {
                   if (result.getData() == null) {
                       results = new Uri[]{cameraImageUri};
                   } else {
                       results = WebChromeClient.FileChooserParams.parseResult(
                           result.getResultCode(), result.getData());
                   }
               }
               if (filePathCallback != null) {
                   filePathCallback.onReceiveValue(results);
                   filePathCallback = null;
               }
           });

       webView.setWebChromeClient(new WebChromeClient() {
           @Override
           public boolean onShowFileChooser(WebView webView,
               ValueCallback<Uri[]> filePathCallback,
               FileChooserParams fileChooserParams) {
               MainActivity.this.filePathCallback = filePathCallback;
               Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
               if (cameraIntent.resolveActivity(getPackageManager()) != null) {
                   try {
                       File photoFile = createImageFile();
                       cameraImageUri = FileProvider.getUriForFile(
                           MainActivity.this,
                           "YOUR_APP_ID.provider", // ← পরিবর্তন করুন
                           photoFile);
                       cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, cameraImageUri);
                   } catch (IOException e) {
                       Toast.makeText(MainActivity.this, "Camera error", Toast.LENGTH_SHORT).show();
                   }
               }
               Intent galleryIntent = new Intent(Intent.ACTION_GET_CONTENT);
               galleryIntent.addCategory(Intent.CATEGORY_OPENABLE);
               galleryIntent.setType("image/*");
               Intent chooser = new Intent(Intent.ACTION_CHOOSER);
               chooser.putExtra(Intent.EXTRA_INTENT, galleryIntent);
               chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[]{cameraIntent});
               fileChooserLauncher.launch(chooser);
               return true;
           }
       });
   }

   private File createImageFile() throws IOException {
       String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
       File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
       return File.createTempFile("IMG_" + timeStamp, ".jpg", storageDir);
   }

   private void requestCameraPermission() {
       if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
           if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
                   != PackageManager.PERMISSION_GRANTED) {
               requestPermissions(new String[]{Manifest.permission.CAMERA}, 100);
           }
       }
   }

   @Override
   public void onBackPressed() {
       if (webView.canGoBack()) { webView.goBack(); }
       else { super.onBackPressed(); }
   }
}
STEP 03
res/xml/file_paths.xml
res/xml/file_paths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-files-path
        name="images"
        path="Pictures/" />
</paths>
STEP 04
AndroidManifest.xml — Permissions + Provider
AndroidManifest.xml
<!-- application এর আগে -->
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.CAMERA"/>

<!-- activity এর আগে, application এর ভেতরে -->
<provider
    android:name="androidx.core.content.FileProvider"
    android:authorities="${applicationId}.provider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths" />
</provider>
MODULE 3 — Location Permission
MANIFEST
Location Permission যোগ করুন
AndroidManifest.xml
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
JAVA
WebSettings + GeoLocation Permission
MainActivity.java — Location এর কোড
// WebSettings এ যোগ করুন
webSettings.setGeolocationEnabled(true);

// WebChromeClient এর ভেতরে
@Override
public void onGeolocationPermissionsShowPrompt(final String origin,
    final GeolocationPermissions.Callback callback) {
    callback.invoke(origin, true, false); // ✅ অনুমতি দিন
}

// Permission Request (onCreate এ)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
    (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION)
        != PackageManager.PERMISSION_GRANTED)) {
    requestPermissions(new String[]{
        Manifest.permission.ACCESS_FINE_LOCATION,
        Manifest.permission.ACCESS_COARSE_LOCATION
    }, REQUEST_PERMISSIONS);
}
MODULE 4 — Firebase Push Notification
SETUP
Firebase Project Setup
  • 1Firebase Console → Login করুন → Create Project
  • 2Android App যোগ করুন → Package Name দিন
  • 3google-services.json ডাউনলোড করুন
  • 4Android Studio তে Project → app → src ফোল্ডারে paste করুন
GRADLE
build.gradle — Dependencies
build.gradle (Project) — plugins
id 'com.google.gms.google-services' version '4.4.4' apply false
build.gradle (App) — plugins + dependencies
// plugins এর ভেতরে
id 'com.google.gms.google-services'

// dependencies এর ভেতরে
implementation platform('com.google.firebase:firebase-bom:34.12.0')
implementation 'com.google.firebase:firebase-analytics'
implementation 'com.google.firebase:firebase-messaging:23.0.0'
SERVICE
MyFirebaseService.java — Notification দেখাবে
MainActivity এর পাশে নতুন Java Class তৈরি করুন: MyFirebaseService
MyFirebaseService.java
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.os.Build;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;

public class MyFirebaseService extends FirebaseMessagingService {

   @Override
   public void onMessageReceived(RemoteMessage remoteMessage) {
       String title = remoteMessage.getNotification().getTitle();
       String body  = remoteMessage.getNotification().getBody();
       showNotification(title, body);
   }

   private void showNotification(String title, String message) {
       String channelId = "CHANNEL_ID";
       if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
           NotificationChannel channel = new NotificationChannel(
               channelId, "Default Channel", NotificationManager.IMPORTANCE_HIGH);
           getSystemService(NotificationManager.class).createNotificationChannel(channel);
       }
       NotificationCompat.Builder builder =
           new NotificationCompat.Builder(this, channelId)
               .setSmallIcon(R.drawable.logo)
               .setContentTitle(title)
               .setContentText(message)
               .setPriority(NotificationCompat.PRIORITY_HIGH);
       NotificationManagerCompat.from(this).notify(1, builder.build());
   }
}
SERVER
Server Side — PHP Files
Database এর user টেবিলে token column যোগ করুন — type: longtext
token_update.php
<?php
include("dbcon.php");
if(isset($_REQUEST['phone']) AND isset($_REQUEST['token'])){
    $phone = mysqli_real_escape_string($con, $_REQUEST['phone']);
    $token = mysqli_real_escape_string($con, $_REQUEST['token']);
    mysqli_query($con, "UPDATE `user` SET `token`='$token' WHERE `phone`='$phone'");
}
?>
BONUS
No Internet Page — no_internet.html
এই ফাইলটি app → src → main → assets ফোল্ডারে রাখুন। ফোল্ডার না থাকলে তৈরি করুন।
assets/no_internet.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>No Internet</title>
    <style>
        body {
            display: flex; flex-direction: column;
            justify-content: center; align-items: center;
            height: 100vh; background: #f5f5f5;
            font-family: 'Roboto', Arial, sans-serif;
            text-align: center; padding: 20px;
        }
        .icon { font-size: 70px; margin-bottom: 15px;
            animation: float 3s ease-in-out infinite; }
        h2 { color: #e74c3c; font-size: 24px; margin-bottom: 10px; }
        p  { color: #666; font-size: 16px; margin-bottom: 30px; }
        @keyframes float {
            0%   { transform: translateY(0px); }
            50%  { transform: translateY(-10px); }
            100% { transform: translateY(0px); }
        }
    </style>
</head>
<body>
    <div class="icon">📡❌</div>
    <h2>No Internet Connection</h2>
    <p>Please check your internet connection and try again.</p>
</body>
</html>
Java তে Internet check করুন: webView.loadUrl("file:///android_asset/no_internet.html");
QUICK REF
পরিবর্তন করার জিনিসগুলো — Checklist
অবশ্যই পরিবর্তন করুন
  • Website URL — loadUrl(...)
  • App ID / Package Name
  • Logo — Drawable ফোল্ডারে
  • Firebase Project ID
একবারই করতে হবে
  • Theme → NoActionBar (দুটো জায়গায়)
  • google-services.json পেস্ট
  • file_paths.xml তৈরি
  • no_internet.html → assets ফোল্ডারে