কম্পিউটার টিউটোরিয়াল

Android-এ প্রতিদিন নির্দিষ্ট সময়ে নোটিফিকেশন তৈরি করার পদ্ধতি

প্রতিদিন নিশ্চিত সময়ে নোটিফিকেশন (Daily Recurring)

এই গাইডটি দেখাবে কীভাবে প্রতিদিন একটি নির্দিষ্ট সময়ে (যেমন সকাল ৯:০০) নোটিফিকেশন দেখাতে হয়। আমরা AlarmManager.setRepeating() ব্যবহার করব দৈনিক রিপিটের জন্য।

প্রধান পার্থক্য: Expiration vs Daily

ফিচারExpiration (একবার)Daily Recurring
Alarm TypesetExact / setExactAndAllowWhileIdlesetRepeating
ট্রিগারএকবারপ্রতিদিন (interval = 24h)
Use Caseঅ্যাপয়েন্টমেন্ট, ডেডলাইনদৈনিক রিমাইন্ডার, হ্যাবিট ট্র্যাকার

ধাপ ১: প্রজেক্ট সেটআপ

Package: com.example.dailynotification

ধাপ ২: লেআউট (activity_main.xml)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:tools="https://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="দৈনিক নোটিফিকেশন সময় সেট করুন"
        android:textSize="18sp"
        android:layout_marginBottom="16dp"/>

    <Button
        android:id="@+id/btnTimePicker"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="সময় বেছে নিন"
        android:onClick="showTimePicker"/>

    <Button
        android:id="@+id/btnEnable"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:text="দৈনিক নোটিফিকেশন চালু করুন"
        android:onClick="enableDailyNotification"/>

    <TextView
        android:id="@+id/tvTime"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:text="নির্বাচিত: ০৯:০০ AM"
        android:textSize="16sp"/>

</LinearLayout>

ধাপ ৩: MainActivity.java

package com.example.dailynotification;

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.TimePickerDialog;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import java.util.Calendar;

public class MainActivity extends AppCompatActivity 
        implements TimePickerDialog.OnTimeSetListener {

    private static final String PREFS = "DailyNotifPrefs";
    private static final String KEY_HOUR = "hour";
    private static final String KEY_MINUTE = "minute";
    private static final String KEY_ENABLED = "enabled";

    private TextView tvTime;
    private int hour = 9, minute = 0;
    private AlarmManager alarmManager;
    private PendingIntent pendingIntent;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        tvTime = findViewById(R.id.tvTime);
        alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
        
        loadPrefs();
        updateTimeDisplay();
    }

    public void showTimePicker(View view) {
        new TimePickerDialog(this, this, hour, minute, true).show();
    }

    @Override
    public void onTimeSet(android.widget.TimePicker view, int hourOfDay, int minute) {
        this.hour = hourOfDay;
        this.minute = minute;
        savePrefs();
        updateTimeDisplay();
    }

    public void enableDailyNotification(View view) {
        scheduleDailyAlarm();
        saveEnabled(true);
        findViewById(R.id.btnEnable).setEnabled(false);
        findViewById(R.id.btnTimePicker).setEnabled(false);
    }

    private void scheduleDailyAlarm() {
        Intent intent = new Intent(this, DailyNotificationService.class);
        pendingIntent = PendingIntent.getService(
                this, 0, intent, PendingIntent.FLAG_IMMUTABLE);

        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.HOUR_OF_DAY, hour);
        calendar.set(Calendar.MINUTE, minute);
        calendar.set(Calendar.SECOND, 0);

        // যদি আজকের সময় পাস হয়ে গেছে, পরের দিন থেকে শুরু
        if (calendar.getTimeInMillis() <= System.currentTimeMillis()) {
            calendar.add(Calendar.DAY_OF_MONTH, 1);
        }

        long interval = AlarmManager.INTERVAL_DAY; // 24 ঘণ্টা

        if (alarmManager != null) {
            alarmManager.setRepeating(
                    AlarmManager.RTC_WAKEUP,
                    calendar.getTimeInMillis(),
                    interval,
                    pendingIntent
            );
        }
    }

    private void loadPrefs() {
        SharedPreferences prefs = getSharedPreferences(PREFS, MODE_PRIVATE);
        hour = prefs.getInt(KEY_HOUR, 9);
        minute = prefs.getInt(KEY_MINUTE, 0);
    }

    private void savePrefs() {
        getSharedPreferences(PREFS, MODE_PRIVATE).edit()
                .putInt(KEY_HOUR, hour)
                .putInt(KEY_MINUTE, minute)
                .apply();
    }

    private void saveEnabled(boolean enabled) {
        getSharedPreferences(PREFS, MODE_PRIVATE).edit()
                .putBoolean(KEY_ENABLED, enabled).apply();
    }

    private void updateTimeDisplay() {
        String amPm = hour >= 12 ? "PM" : "AM";
        int displayHour = hour % 12 == 0 ? 12 : hour % 12;
        tvTime.setText(String.format("নির্বাচিত: %02d:%02d %s", displayHour, minute, amPm));
    }
}

ধাপ ৪: DailyNotificationService.java

package com.example.dailynotification;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.Build;
import android.os.IBinder;
import androidx.core.app.NotificationCompat;

public class DailyNotificationService extends Service {

    private static final String CHANNEL_ID = "daily_notif_channel";
    private static final String DEFAULT_CHANNEL = "default";
    private static final int NOTIFICATION_ID = 1001;

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        showNotification();
        stopSelf();
        return START_NOT_STICKY;
    }

    private void showNotification() {
        Intent notificationIntent = new Intent(this, MainActivity.class);
        notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);

        PendingIntent pendingIntent = PendingIntent.getActivity(
                this, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE);

        NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);

        NotificationCompat.Builder builder = new NotificationCompat.Builder(this, DEFAULT_CHANNEL)
                .setContentTitle("দৈনিক রিমাইন্ডার")
                .setContentText("আপনার দৈনিক কাজের সময়! ⏰")
                .setSmallIcon(R.drawable.ic_launcher_foreground)
                .setContentIntent(pendingIntent)
                .setAutoCancel(true)
                .setPriority(NotificationCompat.PRIORITY_HIGH)
                .setCategory(NotificationCompat.CATEGORY_REMINDER);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel(
                    CHANNEL_ID, "দৈনিক নোটিফিকেশন", 
                    NotificationManager.IMPORTANCE_HIGH);
            channel.setDescription("প্রতিদিন নির্দিষ্ট সময়ে আসবে");
            manager.createNotificationChannel(channel);
            builder.setChannelId(CHANNEL_ID);
        }

        manager.notify(NOTIFICATION_ID, builder.build());
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}

ধাপ ৫: BootCompleteReceiver.java (রিবুট Efter রিস্টার্ট)

package com.example.dailynotification;

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Build;
import java.util.Calendar;

public class BootCompleteReceiver extends BroadcastReceiver {
    private static final String PREFS = "DailyNotifPrefs";
    private static final String KEY_HOUR = "hour";
    private static final String KEY_MINUTE = "minute";
    private static final String KEY_ENABLED = "enabled";

    @Override
    public void onReceive(Context context, Intent intent) {
        if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
            SharedPreferences prefs = context.getSharedPreferences(PREFS, Context.MODE_PRIVATE);
            if (prefs.getBoolean(KEY_ENABLED, false)) {
                int hour = prefs.getInt(KEY_HOUR, 9);
                int minute = prefs.getInt(KEY_MINUTE, 0);
                scheduleDaily(context, hour, minute);
            }
        }
    }

    private void scheduleDaily(Context context, int hour, int minute) {
        Intent serviceIntent = new Intent(context, DailyNotificationService.class);
        PendingIntent pendingIntent = PendingIntent.getService(
                context, 0, serviceIntent, PendingIntent.FLAG_IMMUTABLE);

        AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.HOUR_OF_DAY, hour);
        calendar.set(Calendar.MINUTE, minute);
        calendar.set(Calendar.SECOND, 0);

        if (calendar.getTimeInMillis() <= System.currentTimeMillis()) {
            calendar.add(Calendar.DAY_OF_MONTH, 1);
        }

        if (alarmManager != null) {
            alarmManager.setRepeating(
                    AlarmManager.RTC_WAKEUP,
                    calendar.getTimeInMillis(),
                    AlarmManager.INTERVAL_DAY,
                    pendingIntent
            );
        }
    }
}

ধাপ ৬: AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
    package="com.example.dailynotification">

    <uses-permission android:name="android.permission.VIBRATE" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    <uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.DailyNotification">

        <service
            android:name=".DailyNotificationService"
            android:enabled="true"
            android:exported="false" />

        <receiver
            android:name=".BootCompleteReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
        </receiver>

        <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>

গুরুত্বপূর্ণ: setRepeating() এর লিমিটেশন (API 19+)

Android 4.4 (KitKat) থেকে `setRepeating()` ইনএক্সেক্ট হয়ে গেছে — সিস্টেম ব্যাটারি বাঁচাতে একাধিক অ্যাপের অ্যালার্ম বাচ করে একসাথে ফায়ার করে। এক্স্যাক্ট টাইমিং দরকার হলে:

বিকল্প ১: setExact + Reschedule (প্রতি এক্সিকিউশনে)

// Service-এ onStartCommand-এ
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    showNotification();
    rescheduleNextDay(); // পরবর্তী দিনের জন্য Exact সেট করুন
    return START_NOT_STICKY;
}

private void rescheduleNextDay() {
    AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
    Intent intent = new Intent(this, DailyNotificationService.class);
    PendingIntent pi = PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_IMMUTABLE);
    
    Calendar cal = Calendar.getInstance();
    cal.add(Calendar.DAY_OF_MONTH, 1);
    cal.set(Calendar.HOUR_OF_DAY, 9); // ফিক্সড আওর
    cal.set(Calendar.MINUTE, 0);
    cal.set(Calendar.SECOND, 0);
    
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        am.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), pi);
    } else {
        am.setExact(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), pi);
    }
}

বিকল্প ২: WorkManager (সুপারিশ করা, আধুনিক)

// build.gradle
implementation "androidx.work:work-runtime-ktx:2.9.0"

// Worker
public class DailyNotifyWorker extends Worker {
    public DailyNotifyWorker(@NonNull Context context, @NonNull WorkerParameters params) {
        super(context, params);
    }
    @NonNull
    @Override
    public Result doWork() {
        // নোটিফিকেশন দেখান
        return Result.success();
    }
}

// Schedule (MainActivity-এ)
PeriodicWorkRequest request = new PeriodicWorkRequest.Builder(
        DailyNotifyWorker.class, 24, TimeUnit.HOURS)
        .setInitialDelay(calculateInitialDelay(), TimeUnit.MILLISECONDS)
        .build();
WorkManager.getInstance(this).enqueue(request);

পরীক্ষা চেকলিস্ট

  1. সময় সেট করুন → "চালু করুন" চাপুন
  2. ডিভাইস রিবুট → পরের দিন সেট সময়ে নোটিফিকেশন আসছে কিনা
  3. ব্যাটারি সেভার অন থাকলে ও কাজ করছে কিনা (Exact alarm permission দরকার)
  4. অ্যাপ ফোর্স স্টপ করলে রিবুটে রিস্টোর হচ্ছে কিনা
  1. অ্যান্ড্রয়েড অ্যাপে WebView তৈরির পদ্ধতি

  2. অ্যান্ড্রয়েড অ্যাপে TextToSpeech তৈরি করার পদ্ধতি

  3. Facebook-এ Android অ্যাপ তৈরি করার পদ্ধতি

  4. এন্ড্রয়েড ৮ Snooze মাস্টার করুন: ভালো ঘুমের জন্য নোটিফিকেশন ম্যানেজ করুন