ইনকামিং কল অটোমেটিক রিসিভ করার চ্যালেঞ্জ এবং সমাধান
গুরুত্বপূর্ণ সতর্কতা: অ্যান্ড্রয়েড 4.1 (API 16) থেকে শুরু করে, থার্ড-পার্টি অ্যাপ্লিকেশনগুলির জন্য প্রোগ্রামেটিক্যালি কল রিসিভ করা সিকিউরিটি কারণে সীমাবদ্ধ। এই টিউটোরিয়ালটি ঐতিহাসিক রেফারেন্স এবং সীমিত ব্যবহারের ক্ষেত্রে (সিস্টেম অ্যাপ, রুট করা ডিভাইস) জন্য।
ধাপ ১: প্রজেক্ট তৈরি করুন
Android Studio-তে নতুন প্রজেক্ট।
ধাপ ২: লেআউট (activity_main.xml)
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:tools="https://schemas.android.com/tools"
xmlns:app="https://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="কল রিসিভার ডেমো"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
ধাপ ৩: পারমিশনস (AndroidManifest.xml)
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
package="com.app.sample">
<!-- কল অবস্থা পড়তে -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<!-- কল রিসিভ করার চেষ্টা (সিস্টেম লেভেল) -->
<uses-permission android:name="android.permission.ANSWER_PHONE_CALLS" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<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/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- কল স্ক্রিনিং সার্ভিস (Android 10+) -->
<service android:name=".CallScreeningService"
android:permission="android.permission.BIND_SCREENING_SERVICE"
android:exported="true">
<intent-filter>
<action android:name="android.telecom.CallScreeningService" />
</intent-filter>
</service>
</application>
</manifest>
ধাপ ৪: CallScreeningService (Android 10+ API 29+) - আধুনিক পদ্ধতি
package com.app.sample;
import android.telecom.CallScreeningService;
import android.telecom.Call.Details;
public class CallScreeningService extends CallScreeningService {
@Override
public void onScreenCall(Details callDetails) {
// কলারের নম্বর চেক করুন
String phoneNumber = callDetails.getHandle() != null ?
callDetails.getHandle().getSchemeSpecificPart() : "";
CallResponse.Builder responseBuilder = new CallResponse.Builder();
// নির্দিষ্ট নম্বর অটো-রিসিভ (যেমন ইমার্জেন্সি কনট্যাক্ট)
if (isTrustedNumber(phoneNumber)) {
responseBuilder.setResponse(CallResponse.RESPONSE_ALLOW)
.setDisallowCall(false);
} else {
responseBuilder.setResponse(CallResponse.RESPONSE_SILENCE)
.setRejectWithMessage("ব্যস্ত আছি, পরে কল করুন");
}
respondToCall(callDetails, responseBuilder.build());
}
private boolean isTrustedNumber(String number) {
// আপনার লজিক এখানে (ডাটাবেস, শেয়ার্ড প্রিফারেন্স ইত্যাদি থেকে)
return number != null && number.startsWith("+88017"); // উদাহরণ
}
}
ধাপ ৫: পুরানো পদ্ধতি (API 16-28) - শুধু রেফারেন্স
// এই পদ্ধতিটি সাধারণ অ্যাপে কাজ করে না (সিকিউরিটি এক্সেপশন)
public void acceptCallLegacy() {
try {
// HEADSETHOOK কী ইভেন্ট অনুকরণ (রুট বা সিস্টেম অ্যাপ দরকার)
Runtime.getRuntime().exec("input keyevent " + KeyEvent.KEYCODE_HEADSETHOOK);
} catch (IOException e) {
// ফলব্যাক: ব্রডকাস্ট ইমিটেট
Intent btnDown = new Intent(Intent.ACTION_MEDIA_BUTTON)
.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_HEADSETHOOK));
Intent btnUp = new Intent(Intent.ACTION_MEDIA_BUTTON)
.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK));
sendOrderedBroadcast(btnDown, "android.permission.CALL_PRIVILEGED");
sendOrderedBroadcast(btnUp, "android.permission.CALL_PRIVILEGED");
}
}
বৈধ বিকল্পসমূহ (Play Store কমপ্লায়েন্ট)
১. Call Screening (Android 10+) - সুপারিশড
- ব্যবহারকারীকে কল ব্লক/সাইলেন্স/রিসিভ করার অনুমতি দিন
- CallScreeningService এক্সটেন্ড করুন
- Manifest-এ BIND_SCREENING_SERVICE পারমিশন
২. Telecom Framework (Android 6.0+)
- ConnectionService বাস্তবায়ন করুন
- VoIP অ্যাপের জন্য উপযুক্ত
- সিস্টেম ডায়ালারের সাথে ইন্টিগ্রেশন
৩. Companion Device Pairing
- ব্লুটুথ ডিভাইস (স্মার্টওয়াচ) দিয়ে কল কন্ট্রোল
- CompanionDeviceManager ব্যবহার করুন
৪. Notification Actions
// নোটিফিকেশনে "রিসিভ" বাটন যোগ
NotificationCompat.Action answerAction = new NotificationCompat.Action.Builder(
R.drawable.ic_answer, "রিসিভ", answerPendingIntent).build();
Notification notification = new NotificationCompat.Builder(context, CHANNEL_ID)
.setContentTitle("ইনকামিং কল")
.setContentText(phoneNumber)
.addAction(answerAction)
.setCategory(NotificationCompat.CATEGORY_CALL)
.build();
MainActivity.java - কল মনিটরিং
package com.app.sample;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
public class MainActivity extends AppCompatActivity {
private TelephonyManager telephonyManager;
private PhoneStateListener phoneStateListener;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startCallMonitoring();
}
private void startCallMonitoring() {
telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
phoneStateListener = new PhoneStateListener() {
@Override
public void onCallStateChanged(int state, String incomingNumber) {
super.onCallStateChanged(state, incomingNumber);
switch (state) {
case TelephonyManager.CALL_STATE_RINGING:
// ইনকামিং কল - UI আপডেট, নোটিফিকেশন, লগিং
showIncomingCallUI(incomingNumber);
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
// কল সংযুক্ত
break;
case TelephonyManager.CALL_STATE_IDLE:
// কল শেষ
hideIncomingCallUI();
break;
}
}
};
telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
}
private void showIncomingCallUI(String number) {
// ফুলস্ক্রিন অ্যাক্টিভিটি বা হাই প্রায়োরিটি নোটিফিকেশন দেখান
}
private void hideIncomingCallUI() {
// UI লুকান
}
@Override
protected void onDestroy() {
super.onDestroy();
if (telephonyManager != null && phoneStateListener != null) {
telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE);
}
}
}