Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .idea/gradle.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# AndroidD20
//Tại em đang sạc nên nó nhận broadcast pin thay đổi liên tục ạ ;-;

![ezgif com-gif-maker](https://user-images.githubusercontent.com/84552830/184516907-b01d1e27-923a-4ddb-8811-a1a22fad7818.gif)
12 changes: 9 additions & 3 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,21 @@ android {
kotlinOptions {
jvmTarget = '1.8'
}
buildFeatures{
viewBinding = true
}
}

dependencies {

implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.4.2'
implementation 'com.google.android.material:material:1.6.1'
implementation 'androidx.core:core-ktx:1.8.0'
implementation 'androidx.appcompat:appcompat:1.5.0'
implementation 'com.google.android.material:material:1.7.0-alpha03'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.navigation:navigation-fragment-ktx:2.5.1'
implementation 'androidx.navigation:navigation-ui-ktx:2.5.1'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
implementation "com.google.android.gms:play-services-location:20.0.0"
}
19 changes: 19 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,16 @@
xmlns:tools="http://schemas.android.com/tools"
package="com.sudo.androidd20">

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

<!-- Luôn thêm quyền này từ android 11 trở lên khi xin quyền lấy vị trí-->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

<!-- Khi dùng ForegroundService cần thêm quyền này-->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

<application
android:name=".MyApp"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
Expand All @@ -22,6 +31,16 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

<service android:name=".services.LocationNotificationService" />

<receiver
android:name=".broadcastReceivers.BatteryBroadcastReceiver"
android:exported="false"> <!--Phải thành false để thằng lấy thông tin vị trí từ gps mới sử dụng được-->
<intent-filter>
<action android:name="android.intent.action.BATTERY_CHANGED" />
</intent-filter>
</receiver>
</application>

</manifest>
116 changes: 113 additions & 3 deletions app/src/main/java/com/sudo/androidd20/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -1,11 +1,121 @@
package com.sudo.androidd20

import androidx.appcompat.app.AppCompatActivity
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.PackageManager
import android.location.LocationManager
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.WindowCompat
import com.sudo.androidd20.broadcastReceivers.BatteryBroadcastReceiver
import com.sudo.androidd20.services.LocationNotificationService
import com.sudo.androidd20.utils.Permission
import com.sudo.androidd20.databinding.ActivityMainBinding
import com.sudo.androidd20.dialogs.RequestOpenGpsDialog
import com.google.android.gms.location.FusedLocationProviderClient
import com.google.android.gms.location.LocationServices


class MainActivity : AppCompatActivity(), BatteryBroadcastReceiver.BatteryChangedListener {

private lateinit var binding: ActivityMainBinding
private var isStartedLocationService = false

class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
WindowCompat.setDecorFitsSystemWindows(window, false)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

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

setOnClicked()

//Từ Android 8 (Oreo)
registerReceiver(BatteryBroadcastReceiver(), IntentFilter(Intent.ACTION_BATTERY_CHANGED))
}

private fun setOnClicked() {
binding.btnGetLocation.setOnClickListener {
getLocation()
}
}

@SuppressLint("MissingPermission")
private fun getLocation() {
//Check nếu chưa cấp quyền truy cập vị trí sẽ yêu cầu cấp quyền:
if (!Permission.isGrantedLocationPermission(this))
Permission.grantLocationPermission(this)
else {
if (!isStartedLocationService)
startService(Intent(this, LocationNotificationService::class.java))

//Mở hộp thoại yêu cầu người dùng mở GPS:
requestTurnGpsOn()


//Đoạn này em xem trên android.dev là để lấy vị trí ;-;
val fusedLocationClient: FusedLocationProviderClient =
LocationServices.getFusedLocationProviderClient(this)

fusedLocationClient.lastLocation.addOnCompleteListener(this) {
val location = it.result
if (location != null)
binding.tvLocation.text = location.run {
"Kinh độ: $longitude\nVĩ độ: $latitude"
}
}
}
}

//Kiểm tra khi người dùng chọn lựa có cấp quyền không:
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//permission granted
} else {
//permission denied
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
}

//Cái này em xem trên stackoverflow:
private fun requestTurnGpsOn() {
val lm = getSystemService(Context.LOCATION_SERVICE) as LocationManager
var gpsEnabled = false
var networkEnabled = false

try {
gpsEnabled = lm.isProviderEnabled(LocationManager.GPS_PROVIDER)
} catch (e: Exception) {
e.printStackTrace()
}

try {
networkEnabled = lm.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
} catch (e: Exception) {
e.printStackTrace()
}

if (!gpsEnabled && !networkEnabled) {
RequestOpenGpsDialog(this).create().show()
}
}

override fun onDestroy() {
//Tắt đi nếu không muốn bị chạy ngầm :v
stopService(Intent(this, LocationNotificationService::class.java))
unregisterReceiver(BatteryBroadcastReceiver())
super.onDestroy()
}

//Override lại thằng notifyBatteryChanged của BatteryBroadcastReceiver để update pin hiển thị trên màn hình
@SuppressLint("SetTextI18n")
override fun notifyBatteryChanged(currentBatteryLevel: Int) {
binding.tvCurrentBatteryLevel.text = "$currentBatteryLevel%"
}
}
29 changes: 29 additions & 0 deletions app/src/main/java/com/sudo/androidd20/MyApp.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.sudo.androidd20

import android.app.Application
import android.app.NotificationChannel
import android.app.NotificationManager
import android.os.Build
import com.sudo.androidd20.others.Const.CHANNEL_ID


//Cần tạo cái channel ngay đầu ứng dụng để sử dụng nếu khônh muốn lỗi :)
class MyApp : Application() {
override fun onCreate() {
//Từ Android 8 trở lên cần dùng Channel:
// Create the NotificationChannel, but only on API 26+ because
// the NotificationChannel class is new and not in the support library
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val locationNotificationChannel = NotificationChannel(
CHANNEL_ID,
"Location Notification",
NotificationManager.IMPORTANCE_DEFAULT
)

val notificationManager = getSystemService(NotificationManager::class.java)
notificationManager.createNotificationChannel(locationNotificationChannel)
}

super.onCreate()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.sudo.androidd20.broadcastReceivers

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.os.BatteryManager
import android.widget.Toast
import com.sudo.androidd20.MainActivity

class BatteryBroadcastReceiver : BroadcastReceiver() {

interface BatteryChangedListener {
fun notifyBatteryChanged(currentBatteryLevel: Int)
}

//Thực hiện các hành động khi nhận được broadcast phản hồi từ hệ thống:
override fun onReceive(context: Context?, intent: Intent?) {
//Mặc dù khi registerReceiver đã xác định được bắt sự kiện do Action nào trả về rồi nhưng cần check xem có đúng không để tránh bị UnsafeProtectedBroadcastReceiver
//- kiểu có luồng dữ liệu sai lệch không do hệ thống gửi trong khi thay đổi pin là Broadcast do hệ thống cung cấp
when (intent?.action) {
Intent.ACTION_BATTERY_CHANGED -> {
val currentBatteryLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0)
val batteryChangedListener = context as MainActivity

Toast.makeText(
context,
"Current battery level: $currentBatteryLevel",
Toast.LENGTH_LONG
)
.show()

batteryChangedListener.notifyBatteryChanged(currentBatteryLevel)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.sudo.androidd20.dialogs

import android.content.Context
import android.content.DialogInterface
import android.content.Intent
import android.provider.Settings
import androidx.appcompat.app.AlertDialog
import com.google.android.material.dialog.MaterialAlertDialogBuilder

class RequestOpenGpsDialog(context: Context) : MaterialAlertDialogBuilder(context) {
override fun create(): AlertDialog {
this.setTitle("Open gps")
.setPositiveButton(
"SETTING",
DialogInterface.OnClickListener(openGpsSettingButtonClicked)
)
.setNegativeButton(
"CANCEL",
DialogInterface.OnClickListener(cancelButtonClicked)
)

return super.create()
}

private val cancelButtonClicked =
{ dialog: DialogInterface, _: Int -> dialog.dismiss() }

private val openGpsSettingButtonClicked = { _: DialogInterface, _: Int ->
context.startActivity(
Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)
)
}
}
5 changes: 5 additions & 0 deletions app/src/main/java/com/sudo/androidd20/others/Const.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.sudo.androidd20.others

object Const {
const val CHANNEL_ID = "channel_id"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.sudo.androidd20.services

import android.annotation.SuppressLint
import android.app.Service
import android.content.Intent
import android.os.IBinder
import android.util.Log
import android.widget.RemoteViews
import androidx.core.app.NotificationCompat
import com.sudo.androidd20.others.Const.CHANNEL_ID
import com.sudo.androidd20.R

class LocationNotificationService : Service() {
override fun onCreate() {
Log.i("Services", "onCreate")
super.onCreate()
}

override fun onBind(intent: Intent?): IBinder? {
return null
}

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
sendPushNotification()

//khi bị hệ thống kill thì không cần khởi động lại service
return START_NOT_STICKY
}

@SuppressLint("UnspecifiedImmutableFlag")
private fun sendPushNotification() {

//tạo view cho PushNotification:
val remoteViews = RemoteViews(packageName, R.layout.custom_notification)
remoteViews.setTextViewText(R.id.tvContentText, "Brruh bruh lmao")
remoteViews.setImageViewResource(R.id.imbtnHideNotification, R.drawable.ic_round_close_24)

val locationNotification = NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_round_close_24)
.setCustomContentView(remoteViews)
.setAutoCancel(true)
.build()

startForeground(1, locationNotification)
}

override fun onDestroy() {
Log.i("Service", "onDestroy")
super.onDestroy()
}
}
Loading