Hướng dẫn viết tool gửi tin nhắn SMS trên Android với Kotlin

27/09/2022 - lượt xem
Chia sẻ
 
5/5 - (1 bình chọn)

Gửi tin nhắn SMS tự động trên Android có lẽ là yêu cầu khá cơ bản mà nhiều bạn khi làm Android gặp phải. Code Tu Tam hôm nay sẽ hướng dẫn các bạn xây dựng tool gửi tin nhắn SMS trên Android. Code gửi tin nhắn sms android sẽ được triển khai với Kotlin. CodeTuTam cũng chỉ là dân ngoại đạo và triển khai tool này theo yêu cầu.

Chính vì lẽ vậy, các kiến thức chia sẻ trong bài viết này có thể không phải cách chuẩn tắc, cũng như tối ưu. Tuy nhiên nội dung trong bài được viết dựa trên kiến thức kinh nghiệm có được trong quá trình triển khai của CodeTuTam

Nếu các bạn chưa biết nhiều về Kotlin có thể tham khảo thêm các bài viết

Kotlin là gì

Cú pháp cơ bản của Kotlin

Hướng dẫn tạo dự án tool gửi SMS Android trên Android studio

Tạo project mới trong Android Studio

Để khởi tạo một dự án Android mới, bạn có nhiều cách khác nhau. Để đơn giản có thể sử dụng Android Studio.

Hưỡng dẫn tạo Project mới trong Android studio
Hưỡng dẫn tạo Project mới trong Android studio

Các bạn có thể chọn luôn Basic Activity. Với lựa chọn khi xây dựng tool gửi tin nhắn trong Android thì bàn sẽ có sẵ các màn hình cơ bản để triển khai thay vì phải khởi tạo từ đầu.

Cấu hình cho 1 dự án mới trong Android Studio
Cấu hình cho 1 dự án mới trong Android Studio

Khi khởi tạo dự án với Android studio bạn sẽ thấy các lựa chọn như trên hình. Trong đó

Name: Tên dự án của bạn

Package name: là tên package của dự án, cũng giống như namespace trong PHP vậy

Save Location: Thư mục chứa mã nguồn chương trình

Language: Ngôn ngữ lập trình, trong ví dụ này mình sẽ sử dụng Kotlin. Các bạn có thể sử dụng Java như là một ngôn ngữ chính cho triển khai app Android.

Minimum SDK: là phiên bản Android tối thiểu có thể chạy ứng dụng này.

Cấu trúc thư mục của một project Android

Sau khi khởi tạo dự án chúng ta sẽ thấy một màn hình như sau

Màn hình IDE lập trình Android
Màn hình IDE lập trình Android

Trong đó, cột bên trái là khu vực hiển thị cấu trúc thư mục dự án Android chúng ta đang làm, cột bên phải là nội dung file code đang xem. Giao diện này thì cũng quá quen với các bạn lập trình rồi có lẽ cũng không cần phải nói nhiều nữa.

Theo sự tìm hiểu của mình thì cấu trúc dự án Android sẽ bao gồm các phần như sau:

File AndroidManifest.xml: Trong file này sẽ định nghĩa các cấu hình, quyền truy cập, Service, Activity… của ứng dụng chúng ta đang triển khai

Thư mục Java: là thư mục chứa mã nguồn code chính của dự án Android. Trong này có thể chứa code java và kotlin

Thư mục Res: Res viết tắt của Resouces, là thư mục chứa tài nguyên của tool gửi SMS chúng ta đang triển khai. Trong đó 1 số thư mục như drawable là chứa hình ảnh, layout là chứa các file layout giao diện của ứng dụng… Tệp tin chúng ta cần quan tâm sẽ là activity_main.xml và fragment_first.xml.

Gradle Scripts: Phần này chắc là khai báo các thư viện cấu hình, cũng giống như kiểu package.json hay compose.json thì phải (theo mình hiểu là vậy)

Xây dựng giao diện của ứng dụng gửi SMS với Android

Để cho tiện lợi, chúng ta sẽ sử dụng các giao diện mà template này cung cấp

Cập nhật file cấu hình giao diện ứng dụng gửi tin nhắn

Đầu tiên chúng ta sẽ cần xem nội dung file activity_main.xml

Trong file này có một số view AppBar gì đó, mình cũng không quan tâm lắm, tuy nhiên có 1 button. Button này mình không sử dụng và sẽ xóa đi để tránh rối mắt

Khi đó nội dung file activity_main.xml sẽ có nội dung như sau:

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <com.google.android.material.appbar.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/Theme.Testapp.AppBarOverlay">

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/Theme.Testapp.PopupOverlay" />

    </com.google.android.material.appbar.AppBarLayout>

    <include layout="@layout/content_main" />

</androidx.coordinatorlayout.widget.CoordinatorLayout>

Trong file activity_main.xml chúng ta có thấy 1 lệnh là include layout/content_main. Phần này chính là nội dung hiển thị các Fragment trong Android.

Đến phần này nhiều bạn có thể thắc mắc Fragment là gì hay Activity là gì?

Hiểu đơn giản thì, Activity là một màn hình ứng dụng trong Android. Mỗi 1 màn hình thực thi có thể tương ứng với 1 Activity. Ví dụ như Home Activity, Contact Avitity – nó cũng giống như Page của chúng ta khi làm website vậy.

Vậy còn Fragment là gì? Trong 1 Activity thì có thể có nhiều Fragment. Fragment này hiểu nôm na cũng giống như các slider ảnh trong lập trình web vậy. Mỗi Fragment có thể đảm nhiệm các nhiệm vu, nội dung khác nhau. Các Fragment trong 1 Activity có thể ẩn hiện tùy ý để đạt được mục đích

Cả Fragment hay Activity đều sử dụng layout được cấu hình trong file XML.

Tiếp đến chúng ta xem xét file fragment_first.xml, đây chính là file cấu hình giao diện của Fragment First

Trong file này ta thấy có 1 TextView và Button. Textview có lẽ chúng ta cũng không cần thiết lắm nên sẽ loại bỏ đi.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".FirstFragment">



    <Button
        android:id="@+id/button_first"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Start"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        />
</androidx.constraintlayout.widget.ConstraintLayout>

Về cơ bản giao diện của ứng dụng gửi SMS trên Android như vậy là ổn rồi.

Khởi tạo một số file cần thiết trong việc xây dựng ứng dụng gửi tin nhắn

Khởi tạo file SmsService

Để tìm hiểu Service trong Android các bạn có thể Google để hiểu thêm. Tuy nhiên theo góc nhìn non nớt của mình thì Service là một dịch vụ chạy ngầm, ví dụ như Service Play music chả hạn. Do vậy để ứng dụng của chúng ta có thể chạy ngầm, chúng ta cần khai báo 1 Service. Nội dung code cho Service này chúng ta sẽ triển khai trong phần tiếp theo.

Nội dung file SmsService.kt như sau

package com.xs.testapp

import android.app.Service
import android.content.Intent
import android.os.IBinder

class SmsService : Service() {
    override fun onBind(p0: Intent?): IBinder? {
        TODO("Not yet implemented")
    }


}

Cập nhật nội dung file AndroidManifest.xml

Trong file cấu hình của ứng dụng, ta sẽ khai báo Service, xin 1 số quyền cần sử dụng cho ứng dụng

Nội dung file AndroidManifest.xml sau khi cập nhật như sau

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.xs.testapp">
    <uses-permission android:name="android.permission.READ_PHONE_NUMBERS" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.SEND_SMS" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
    <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.Testapp"
        tools:targetApi="31">
        <service
            android:name=".SmsService"
            android:enabled="true"
            android:exported="true"
            >
        </service>
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:label="@string/app_name"
            android:theme="@style/Theme.Testapp.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

 

Trong dó ứng dụng gửi tin nhắn của chúng ta xin một số quyền như sau

READ_PHONE_NUMBERS, READ_PHONE_STATE: để đọc thông tin về điện thoại, lấy thông tin về sim, có ích nếu như điện thoại bạn sử dụng 2 sim

SEND_SMS: Quyền cho phép ứng dụng được phép gửi tin nhắn

INTERNET: Quyền cho phép ứng dụng được truy cập internet. Trên thực tế các cấu hình ứng dụng cần phải lấy qua API về thay vì cấu hình ở trực tiếp ứng dụng.

FOREGROUND_SERVICE: Quyền cho phép chạy ứng dụng ở dạng ngầm

Cập nhật nội dung file MainActivity.kt

File MainActivity.kt chính là file code của màn hình chính ứng dụng. Các hạn có thể hiểu rằng hàm onCreate trong file này sẽ được gọi đến khi ứng dụng được khởi chạy, nói cách khác, khi MainActivity này được khởi tạo

Trong hàm này chúng ta thực hiện xin 1 số quyền thực thi ứng dụng

Mã nguồn file MainActivity.kt sẽ như sau

package com.xs.testapp

import android.Manifest
import android.content.pm.PackageManager
import android.os.Bundle
import com.google.android.material.snackbar.Snackbar
import androidx.appcompat.app.AppCompatActivity
import androidx.navigation.findNavController
import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.navigateUp
import androidx.navigation.ui.setupActionBarWithNavController
import android.view.Menu
import android.view.MenuItem
import androidx.core.app.ActivityCompat
import com.xs.testapp.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {

    private lateinit var appBarConfiguration: AppBarConfiguration
    private lateinit var binding: ActivityMainBinding
    private val PERMISSION_REQUEST_CODE = 100
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        setSupportActionBar(binding.toolbar)

        val navController = findNavController(R.id.nav_host_fragment_content_main)
        appBarConfiguration = AppBarConfiguration(navController.graph)
        setupActionBarWithNavController(navController, appBarConfiguration)

        if(!checkPermission()){
            ActivityCompat.requestPermissions(this,
                arrayOf(
                    Manifest.permission.READ_SMS,
                    Manifest.permission.SEND_SMS,
                    Manifest.permission.READ_PHONE_NUMBERS,
                    Manifest.permission.READ_PHONE_STATE
                ),PERMISSION_REQUEST_CODE)
        }
    }
    private fun checkPermission():Boolean{
        return (ActivityCompat.checkSelfPermission(this,
            Manifest.permission.READ_SMS
        ) != PackageManager.PERMISSION_GRANTED
                &&
                ActivityCompat.checkSelfPermission(this,
                    Manifest.permission.READ_PHONE_NUMBERS
                ) != PackageManager.PERMISSION_GRANTED
                &&
                ActivityCompat.checkSelfPermission(this,
                    Manifest.permission.READ_PHONE_STATE
                ) != PackageManager.PERMISSION_GRANTED
                &&
                ActivityCompat.checkSelfPermission(this,
                    Manifest.permission.SEND_SMS
                ) != PackageManager.PERMISSION_GRANTED)

    }

    override fun onCreateOptionsMenu(menu: Menu): Boolean {
        // Inflate the menu; this adds items to the action bar if it is present.
        menuInflater.inflate(R.menu.menu_main, menu)
        return true
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        return when (item.itemId) {
            R.id.action_settings -> true
            else -> super.onOptionsItemSelected(item)
        }
    }

    override fun onSupportNavigateUp(): Boolean {
        val navController = findNavController(R.id.nav_host_fragment_content_main)
        return navController.navigateUp(appBarConfiguration)
                || super.onSupportNavigateUp()
    }
}

Khởi tạo tệp tin Customer.kt

File Customer.kt có thể coi là DTO dùng để chứa thông tin về 1 đối tượng mà chúng ta muốn gửi SMS tới.

Nội dung file Customer.kt như sau

package com.xs.testapp
class Customer(
    var id:String,
    var phone:String, var content:String, var sim:Int = 0
) {
    override fun toString(): String {
        return "$id - $phone - $content - $sim";
    }
}

Trong class Customer chúng ta có các thuộc tính: id, phone, content, sim

Chúng ta sẽ biết được nội dung được gửi tới số điện thoại nào và bởi sim nào (sim 1 hay sim 2)

Khởi tạo class SmsSender

Class này đảm nhiệm việc gửi SMS tới một customer đã xác định

Nội dung file SmsSender.kt như sau

package com.xs.testapp

import android.Manifest
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
import android.telephony.SmsManager
import android.telephony.SubscriptionInfo
import android.telephony.SubscriptionManager
import android.widget.Toast
import androidx.core.app.ActivityCompat
class SmsSender (var context:Context) {
    companion object{
        val SENT = "SMS_SENT"
        val DELIVERED = "SMS_DELIVERED"
    }



    public fun send(customer:Customer){
        val sentIntent:Intent = Intent(SENT);
        sentIntent.putExtra("smsNumber", customer.phone);
        sentIntent.putExtra("customerId", customer.id);
        var sentPI = PendingIntent.getBroadcast(this.context, 0, sentIntent, PendingIntent.FLAG_CANCEL_CURRENT)
        val deliveredIntent:Intent = Intent(DELIVERED);
        deliveredIntent.putExtra("smsNumber", customer.phone);
        deliveredIntent.putExtra("customerId", customer.id);
        var deliveredPI= PendingIntent.getBroadcast(this.context, 0,deliveredIntent,  PendingIntent.FLAG_CANCEL_CURRENT)

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
            val localSubscriptionManager = SubscriptionManager.from(this.context)
            val checkPermission = ActivityCompat.checkSelfPermission(this.context,Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED;
            if (checkPermission){
                Toast.makeText(this.context,"Ứng dụng chưa cấp quyền",Toast.LENGTH_LONG).show();
                return;
            }
            if (localSubscriptionManager.activeSubscriptionInfoCount > 1) {
                val localList: List<*> = localSubscriptionManager.activeSubscriptionInfoList
                val simIndex = customer.sim;
                val simInfo = localList[simIndex] as SubscriptionInfo
                SmsManager.getSmsManagerForSubscriptionId(simInfo.subscriptionId)
                    .sendTextMessage(customer.phone, null, customer.content, sentPI, deliveredPI)
            }
        } else {
            SmsManager.getDefault()
                .sendTextMessage(customer.phone, null, customer.content, sentPI, deliveredPI)
        }
    }
}

Trong SmsSender có 1 hàm chính là hàm send. Hàm send nhận tham số đầu vào là 1 đối tượng customer. Trong hàm send sẽ xử lý để gửi lần lượt qua sim 1 và sim2 nếu điện thoại chạy ứng dụng này có hệ điều hành lớn hơn phiên bản Lollipop.

Trong này cúng ta cũng tìm hiểu về 1 số khái niệm, pendingIntent, SmsManager…

Khởi tạo tệp tin/class SmsService

Trong class SmsService có nhiệm vụ đăng ký BroastcastReceiver và khởi tạo một tiến trình

BroadcastReceiver là gì? BroadcastReceiver các bạn có thể hiểu rằng nó dùng để lắng nghe các thông báo được ném ra từ ứng dụng hoặc hệ thống Android. Cụ thể trong trường hợp tool gửi SMS này là dùng để nhận các thông báo về việc gửi tin nhắn thành công/thất bại hay là tin nhắn được nhận thành công hay chưa

Trong class SmsService cũng khai báo 2 Broastcast cho việc nhận thông tin SMS này.

Ngoài ra còn khai báo 1 Thread được thiết lập vòng lặp while true để gửi tin nhắn liên tục

Tiếp đến, để duy trì trạng thái hoạt động trong cả chế độ nền, Service của chúng ta sử dụng thêm Notification. Các bạn có thể tham khảo đoạn code dưới đây.

package com.xs.testapp

import android.R
import android.app.*
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.Build
import android.os.IBinder
import android.telephony.SmsManager
import android.widget.Toast
import androidx.core.app.NotificationCompat

class SmsService : Service() {
    val CHANNEL_ID = "ForegroundServiceChannel"
    val smsSender by lazy {
        SmsSender(this)
    };
    class SmsLoop(val smsSender: SmsSender,val context: Context): Thread() {
        public override fun run() {
            while (true){
                val customer = Customer("1","0912345678","Test",0);
                smsSender.send(customer)
                Thread.sleep(15000);
            }
        }
    }
    val smsLoop by lazy {
        SmsLoop(smsSender,this);
    }
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        super.onStartCommand(intent, flags, startId)
        createNotificationChannel()
        val notificationIntent = Intent(this, MainActivity::class.java)
        val pendingIntent = PendingIntent.getActivity(
            this,
            0, notificationIntent, 0
        )
        val notification: Notification = NotificationCompat.Builder(this, CHANNEL_ID)
            .setContentTitle("Sms Foreground Service")
            .setContentText("SMS Sender")
            .setSmallIcon(R.drawable.btn_star)
            .setContentIntent(pendingIntent)
            .build()
        startForeground(1, notification)
        this.registerSmsBroast();
        smsLoop.start();

        return START_NOT_STICKY
    }

    fun registerSmsBroast(){
        val sendSMS: BroadcastReceiver = object : BroadcastReceiver() {
            override fun onReceive(arg0: Context?, intent: Intent?) {
                var resultCodeString: String? = null;
                val smsNumber = intent?.getStringExtra("smsNumber")?:"";
                val customerId = intent?.getStringExtra("customerId")?:"";
                when (resultCode) {
                    Activity.RESULT_OK -> {
                        resultCodeString = "RESULT_OK";
                    }
                    SmsManager.RESULT_ERROR_GENERIC_FAILURE -> {
                        resultCodeString = "RESULT_ERROR_GENERIC_FAILURE";
                    }
                    SmsManager.RESULT_ERROR_NO_SERVICE -> {
                        resultCodeString = "RESULT_ERROR_NO_SERVICE";
                    }
                    SmsManager.RESULT_ERROR_NULL_PDU -> {
                        resultCodeString = "RESULT_ERROR_NULL_PDU";
                    }
                    SmsManager.RESULT_ERROR_RADIO_OFF -> {
                        resultCodeString = "RESULT_ERROR_RADIO_OFF";
                    }
                }
                println("sendSMS $resultCodeString-$smsNumber-$customerId");

            }
        }
        val deliverSMS: BroadcastReceiver = object : BroadcastReceiver() {
            override fun onReceive(arg0: Context?, intent: Intent?) {
                val smsNumber = intent?.getStringExtra("smsNumber")?:"";
                val customerId = intent?.getStringExtra("customerId")?:"";
                var resultCodeString:String? = null;
                when (resultCode) {
                    Activity.RESULT_OK ->
                    {
                        resultCodeString = "RESULT_OK"
                    }
                    Activity.RESULT_CANCELED -> {
                        resultCodeString = "RESULT_CANCELED"
                    }
                }
                println(" deliverSMS $resultCodeString-$smsNumber-$customerId");

            }
        }
        this.registerReceiver(sendSMS, IntentFilter(SmsSender.SENT));
        this.registerReceiver(deliverSMS, IntentFilter(SmsSender.DELIVERED));
    }
    private fun createNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val serviceChannel = NotificationChannel(
                CHANNEL_ID,
                "SMS Foreground Service Channel",
                NotificationManager.IMPORTANCE_DEFAULT
            )
            val manager = getSystemService(
                NotificationManager::class.java
            )
            manager.createNotificationChannel(serviceChannel)
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        smsLoop.interrupt()
    }
    override fun onBind(p0: Intent?): IBinder? {
        return null;
    }
}

Trên thực tế khi mình làm, mình sẽ có cài đặt thêm thư viện Retrofit để gọi API lấy dữ liêu thông tin số điện thoại cần gửi, và sử dụng Moshi để decode các json nhận được

Để tìm hiểu về Retrofit các bạn có thể tham khảo thêm tại link sau: https://square.github.io/retrofit/

Như đã nói, trong mã nguồn sử dụng 1 Thread với while true và sleep 15 giây, nghĩa là 15 giây sẽ gửi tin nhắn 1 lần.

Tiếp đến là chúng ta chỉnh sửa file FirstFragment

Cập nhật nội dung file FirstFragment

Trong FirstFragment chúng ta sẽ khởi tạo SmsService bên trên, khi đó Service của chúng ta sẽ chạy ngầm và thực hiện gửi tin nhắn liên tục

Trong FirstFragment chúng ta khai báo sự kiện cho Button btnFirst của chúng ta, Trong sự kiện này sẽ gọi tới hàm startService

Nội dung class FirstFragment như sau

package com.xs.testapp

import android.content.Intent
import android.os.Build
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.navigation.fragment.findNavController
import com.xs.testapp.databinding.FragmentFirstBinding

/**
 * A simple [Fragment] subclass as the default destination in the navigation.
 */
class FirstFragment : Fragment() {

    private var _binding: FragmentFirstBinding? = null

    // This property is only valid between onCreateView and
    // onDestroyView.
    private val binding get() = _binding!!

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {

        _binding = FragmentFirstBinding.inflate(inflater, container, false)
        return binding.root

    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        binding.buttonFirst.setOnClickListener(startListener);

    }
    private val startListener = View.OnClickListener { view->
        Toast.makeText(activity,"Start", Toast.LENGTH_LONG).show();
        val intentService = Intent(activity, SmsService::class.java)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            activity?.startForegroundService(intentService);
        }
        else{
            activity?.startService(intentService);
        }

    }

    private fun checkDualSim(){

    }


    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }
}

Đến bước này bạn đã hoàn thành 99% quá trình xây dựng tool spam sms trên Android.

Đương nhiên để sử dụng thực tế bạn cần cải thiện thêm 1 số phần giúp hoạt động dễ dàng và hiệu quả hơn.

Tuy vậy với hướng dẫn cơ bản này đã giúp các bạn có cái tổng quan về xây dựng một ứng dụng Android cơ bản với Kotlin, và xây dựng tool gửi tin nhắn SMS trên Android nói riêng.

Tổng kết bài viết viết tool spam sms Android

Qua bài viết này CodeTuTam đã chia sẻ kinh nghiệm xây dựng một ứng dụng Android bằng Kotlin từ con số 0. Trên thực tế, CodeTuTam có triển khai thêm 1 số đoạn mã để có thể lấy thông tin từ server về chạy. Các bạn có thể tham khảo đoạn mã CodeTuTam triển khai và mở rộng theo mục đích sử dụng của mình bạn nhé

Nhưng chú ý rằng, việc spam là điều không nên thực hiện. Gọi là tool spam sms, nhưng bạn chỉ nên dùng để gửi tin nhắn chăm sóc khách hàng, hoặc những người chủ động đăng ký nhận tin với bạn thôi. Nếu không thì rất nhiều vấn đề đợi bạn phía trước đấy :)))

Nếu bạn có bất kì câu hỏi hay thắc mắc nào có thể đặt câu hỏi trong phần bình luận. CodeTuTam sẽ hỗ trợ hết sức mình nếu chúng tôi biết. Cảm ơn bạn đã quan tâm và ủng hộ chúng tôi. Đừng quên like và share bài viết để ủng hộ chúng tôi bạn nhé

 

    Liên hệ với chúng tôi

    Để lại thông tin để nhận được các bài viết khác

    5/5 - (1 bình chọn)

    Xem thêm nhiều bài tin mới nhất về Kiến thức

    Xem thêm