Skip to content

Commit

Permalink
Notification permission for stabler toasts (#4)
Browse files Browse the repository at this point in the history
* Upgrades and migrations

* Add POST_NOTIFICATIONS permission for toasts
  • Loading branch information
Shingyx authored Apr 30, 2024
1 parent 13deaee commit a3e4559
Show file tree
Hide file tree
Showing 10 changed files with 79 additions and 53 deletions.
2 changes: 1 addition & 1 deletion .idea/kotlinc.xml

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

1 change: 1 addition & 0 deletions .idea/modules.xml

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

12 changes: 5 additions & 7 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
ext.kotlin_version = '1.6.21'
ext.kotlin_version = '1.9.20'

repositories {
google()
jcenter()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:8.2.2'
classpath 'com.android.tools.build:gradle:8.3.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "de.mannodermaus.gradle.plugins:android-junit5:1.6.2.0"

// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
Expand All @@ -20,10 +18,10 @@ buildscript {
allprojects {
repositories {
google()
jcenter()
mavenCentral()
}
}

tasks.register('clean', Delete) {
delete rootProject.buildDir
delete rootProject.layout.buildDirectory
}
2 changes: 0 additions & 2 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ org.gradle.jvmargs=-Xmx1536m
# Android operating system, and which are packaged with your app's APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
# Automatically convert third-party libraries to use AndroidX
android.enableJetifier=true

# Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
28 changes: 17 additions & 11 deletions wakeonlan/build.gradle
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'de.mannodermaus.android-junit5'
plugins {
id 'com.android.application'
id 'kotlin-android'
id "de.mannodermaus.android-junit5" version "1.10.0.0"
}

def keystorePropertiesFile = rootProject.file("keystore.properties")
def keystoreProperties = new Properties()
Expand Down Expand Up @@ -36,15 +37,20 @@ android {
signingConfig signingConfigs.release
}
}
buildFeatures {
viewBinding true
}
kotlin {
jvmToolchain(17)
}
}

dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.2'
implementation 'com.google.android.material:material:1.2.1'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.1'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'com.google.android.material:material:1.11.0'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.0'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.10.2'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.10.2'
}
1 change: 1 addition & 0 deletions wakeonlan/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

<application
android:allowBackup="true"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ package com.github.shingyx.wakeonlan.ui
import android.Manifest
import android.content.DialogInterface
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import android.view.View
import android.widget.EditText
import androidx.annotation.StringRes
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
Expand All @@ -14,74 +16,92 @@ import com.github.shingyx.wakeonlan.R
import com.github.shingyx.wakeonlan.data.Host
import com.github.shingyx.wakeonlan.data.WifiAddresses
import com.github.shingyx.wakeonlan.data.isMacAddressValid
import com.github.shingyx.wakeonlan.databinding.ActivityMainBinding
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.android.synthetic.main.activity_main.configure
import kotlinx.android.synthetic.main.activity_main.mac_address
import kotlinx.android.synthetic.main.activity_main.name
import kotlinx.android.synthetic.main.activity_main.progress
import kotlinx.android.synthetic.main.activity_main.ssid
import kotlinx.android.synthetic.main.activity_main.turn_on
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch

class MainActivity : AppCompatActivity() {
private val scope = MainScope()

class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
private lateinit var binding: ActivityMainBinding
private lateinit var model: MainViewModel

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)

model = MainViewModel(application)

model.host.observe(this, Observer(this::populateHostUi))
model.turnOnPcResult.observe(this, Observer(this::handleTurnOnResult))

configure.setOnClickListener {
if (checkPermissions()) {
binding.configure.setOnClickListener {
if (checkOrRequestLocationPermissions()) {
configureHost()
}
}

turn_on.setOnClickListener {
if (checkPermissions()) {
binding.turnOn.setOnClickListener {
if (checkOrRequestLocationPermissions()) {
setUiEnabled(false)
scope.launch { model.turnOnPc() }
launch { model.turnOnPc() }
}
}

checkOrRequestPostNotificationsPermission()
}

override fun onDestroy() {
scope.cancel()
cancel()
super.onDestroy()
}

private fun populateHostUi(host: Host?) {
name.text = host?.name ?: "-"
mac_address.text = host?.macAddress ?: "-"
ssid.text = host?.ssid ?: "-"
binding.name.text = host?.name ?: "-"
binding.macAddress.text = host?.macAddress ?: "-"
binding.ssid.text = host?.ssid ?: "-"
}

private fun checkOrRequestLocationPermissions(): Boolean {
return checkOrRequestPermission(
Manifest.permission.ACCESS_FINE_LOCATION,
R.string.location_permission_title,
R.string.location_permission_rationale
)
}

private fun checkOrRequestPostNotificationsPermission(): Boolean {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
return true
}
return checkOrRequestPermission(
Manifest.permission.POST_NOTIFICATIONS,
R.string.notification_permission_title,
R.string.notification_permission_rationale
)
}

private fun checkPermissions(): Boolean {
val permission = Manifest.permission.ACCESS_FINE_LOCATION
private fun checkOrRequestPermission(
permission: String, @StringRes titleResId: Int, @StringRes rationaleResId: Int
): Boolean {
val checkResult = ContextCompat.checkSelfPermission(this, permission)
if (checkResult == PackageManager.PERMISSION_GRANTED) {
return true
}

if (ActivityCompat.shouldShowRequestPermissionRationale(this, permission)) {
MaterialAlertDialogBuilder(this)
.setTitle(R.string.location_permission_title)
.setMessage(R.string.location_permission_rationale)
.setTitle(titleResId)
.setMessage(rationaleResId)
.setPositiveButton(android.R.string.ok) { _, _ ->
ActivityCompat.requestPermissions(this, arrayOf(permission), 0)
ActivityCompat.requestPermissions(this, arrayOf(permission), titleResId)
}
.setCancelable(false)
.show()
} else {
ActivityCompat.requestPermissions(this, arrayOf(permission), 0)
ActivityCompat.requestPermissions(this, arrayOf(permission), titleResId)
}
return false
}
Expand Down Expand Up @@ -141,8 +161,8 @@ class MainActivity : AppCompatActivity() {
}

private fun setUiEnabled(enable: Boolean) {
configure.isEnabled = enable
turn_on.isEnabled = enable
progress.visibility = if (enable) View.INVISIBLE else View.VISIBLE
binding.configure.isEnabled = enable
binding.turnOn.isEnabled = enable
binding.progress.visibility = if (enable) View.INVISIBLE else View.VISIBLE
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,21 @@ import androidx.appcompat.app.AppCompatActivity
import com.github.shingyx.wakeonlan.R
import com.github.shingyx.wakeonlan.data.NetworkHandler
import com.github.shingyx.wakeonlan.data.Storage
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch

private const val ACTION_SEND_PACKET = "ACTION_SEND_PACKET"

class ShortcutActivity : AppCompatActivity() {
class ShortcutActivity : AppCompatActivity(), CoroutineScope by MainScope() {
private val tag = javaClass.simpleName
private val scope = MainScope()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

when (intent.action) {
Intent.ACTION_CREATE_SHORTCUT -> createShortcut()
ACTION_SEND_PACKET -> scope.launch { sendPacket() }
ACTION_SEND_PACKET -> launch { sendPacket() }
else -> Log.w(tag, "Unknown intent action ${intent.action}")
}

Expand Down
2 changes: 2 additions & 0 deletions wakeonlan/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
<string name="pc_turned_on">Your PC has been turned on!</string>
<string name="location_permission_title">Grant location permissions?</string>
<string name="location_permission_rationale">Wake on LAN requires location permissions to access the SSID of your WiFi network. This is used to make sure your device is on the correct network before trying to turn your PC on.</string>
<string name="notification_permission_title">Allow notifications?</string>
<string name="notification_permission_rationale">Without allowing notifications, the widget will operate silently without toast notifications.</string>
<string name="error">Error</string>
<string name="error_null_host">PC has not been selected yet.</string>
<string name="error_cannot_read_wifi_info">Unable to read WiFi info. Is WiFi turned on?</string>
Expand Down

0 comments on commit a3e4559

Please sign in to comment.