Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
Signed-off-by: Ivan Iskandar <[email protected]>
  • Loading branch information
ivaniskandar committed Nov 2, 2019
0 parents commit 906d01a
Show file tree
Hide file tree
Showing 43 changed files with 771 additions and 0 deletions.
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
*.iml
.gradle
/local.properties
/.idea
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Suzu

It plays a sound when you turn on your device, that's it. Get the prebuilt [here](https://github.com/ivaniskandar/suzu/releases).

![preview](art/screenshot.png)
2 changes: 2 additions & 0 deletions app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/build
/release
29 changes: 29 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'

android {
compileSdkVersion 29
defaultConfig {
applicationId "xyz.ivaniskandar.yuzu"
minSdkVersion 24
targetSdkVersion 29
versionCode 1
versionName "1.0.0"
}
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}

dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.core:core-ktx:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'com.google.android.material:material:1.2.0-alpha01'
}
22 changes: 22 additions & 0 deletions app/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
-dontobfuscate
35 changes: 35 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?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="xyz.ivaniskandar.yuzu">

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

<application
android:allowBackup="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:ignore="GoogleAppIndexingWarning">
<activity android:name=".ui.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

<receiver
android:name=".service.BootReceiver"
android:directBootAware="true">
<intent-filter>
<action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
</intent-filter>
</receiver>

<service
android:name="xyz.ivaniskandar.yuzu.service.BootService"
android:directBootAware="true" />
</application>
</manifest>
Binary file added app/src/main/ic_launcher-web.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 17 additions & 0 deletions app/src/main/java/xyz/ivaniskandar/yuzu/service/BootReceiver.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package xyz.ivaniskandar.yuzu.service

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import androidx.core.content.ContextCompat
import xyz.ivaniskandar.yuzu.util.Prefs

class BootReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (intent.action == Intent.ACTION_LOCKED_BOOT_COMPLETED) {
if (Prefs(context).masterEnabled) {
ContextCompat.startForegroundService(context, Intent(context, BootService::class.java))
}
}
}
}
55 changes: 55 additions & 0 deletions app/src/main/java/xyz/ivaniskandar/yuzu/service/BootService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package xyz.ivaniskandar.yuzu.service

import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.NotificationManager.IMPORTANCE_DEFAULT
import android.app.Service
import android.content.Intent
import android.media.MediaPlayer
import android.os.Build
import android.os.IBinder
import androidx.core.app.NotificationCompat
import androidx.core.content.ContextCompat
import xyz.ivaniskandar.yuzu.R
import xyz.ivaniskandar.yuzu.util.getBootMediaPlayer

class BootService : Service() {
companion object {
private const val CHANNEL_GENERAL = "general"
private const val FOREGROUND_ID = 14045
}

private var mediaPlayer: MediaPlayer? = null

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
ContextCompat.getSystemService(this, NotificationManager::class.java)!!.run {
val channel = NotificationChannel(
CHANNEL_GENERAL,
getString(R.string.notification_general_title),
IMPORTANCE_DEFAULT
)
channel.setSound(null, null)
createNotificationChannel(channel)
}
}

val notification = NotificationCompat.Builder(this, CHANNEL_GENERAL).apply {
setShowWhen(false)
setSmallIcon(R.drawable.ic_sound)
setContentTitle(getString(R.string.boot_notification_title))
}.build()
startForeground(FOREGROUND_ID, notification)

mediaPlayer = getBootMediaPlayer(this) { mp ->
mp.release()
stopForeground(true)
}.apply {
prepare()
start()
}
return START_NOT_STICKY
}

override fun onBind(intent: Intent): IBinder? = null
}
55 changes: 55 additions & 0 deletions app/src/main/java/xyz/ivaniskandar/yuzu/ui/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package xyz.ivaniskandar.yuzu.ui

import android.media.MediaPlayer
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.ArrayAdapter
import kotlinx.android.synthetic.main.activity_main.*
import xyz.ivaniskandar.yuzu.R
import xyz.ivaniskandar.yuzu.util.Prefs
import xyz.ivaniskandar.yuzu.util.getBootMediaPlayer

class MainActivity : AppCompatActivity() {
private var mediaPlayer: MediaPlayer? = null

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}

override fun onResume() {
super.onResume()
val prefs = Prefs(this)
val adapter = ArrayAdapter(
this,
R.layout.dropdown_menu_popup_item,
resources.getStringArray(R.array.sound_options_title)
)
sound_option_dropdown.apply {
setText(prefs.soundSelectionTitle)
setAdapter(adapter)
setOnItemClickListener { _, _, i, _ ->
prefs.soundSelectionKey = resources.getStringArray(R.array.sound_options_key)[i]
mediaPlayer?.run {
release()
}
mediaPlayer = getBootMediaPlayer(this@MainActivity)
mediaPlayer!!.apply {
prepare()
start()
}
}
}
mainSwitch.apply {
isChecked = prefs.masterEnabled
setOnCheckedChangeListener { _, b ->
prefs.masterEnabled = b
}
}
}

override fun onStop() {
super.onStop()
mediaPlayer?.release()
}
}
23 changes: 23 additions & 0 deletions app/src/main/java/xyz/ivaniskandar/yuzu/util/MediaHelper.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package xyz.ivaniskandar.yuzu.util

import android.content.Context
import android.media.AudioAttributes
import android.media.MediaPlayer
import android.net.Uri
import xyz.ivaniskandar.yuzu.R

fun getBootMediaPlayer(context: Context, onComplete: (mp: MediaPlayer) -> Unit = {}): MediaPlayer {
val id = when (val key = Prefs(context).soundSelectionKey) {
context.getString(R.string.sound_option_key_home) -> R.raw.home
context.getString(R.string.sound_option_key_pixel) -> R.raw.pixel
context.getString(R.string.sound_option_key_mac) -> R.raw.mac
context.getString(R.string.sound_option_key_vista) -> R.raw.vista
context.getString(R.string.sound_option_key_xp) -> R.raw.xp
else -> throw Exception("wtf, $key is not valid sound option key")
}
return MediaPlayer().apply {
setDataSource(context, Uri.parse("android.resource://${context.packageName}/${id}"))
setAudioAttributes(AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_NOTIFICATION_EVENT).build())
setOnCompletionListener { mp -> onComplete(mp) }
}
}
30 changes: 30 additions & 0 deletions app/src/main/java/xyz/ivaniskandar/yuzu/util/Prefs.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package xyz.ivaniskandar.yuzu.util

import android.content.Context
import androidx.core.content.edit
import xyz.ivaniskandar.yuzu.R

class Prefs(private val context: Context) {
private val prefs = context.createDeviceProtectedStorageContext()
.getSharedPreferences("${context.packageName}.main.config", Context.MODE_PRIVATE)

var masterEnabled: Boolean
get() = prefs.getBoolean(MASTER_ENABLED, false)
set(value) = prefs.edit { putBoolean(MASTER_ENABLED, value) }

var soundSelectionKey: String
get() = prefs.getString(SOUND_SELECTION, context.resources.getStringArray(R.array.sound_options_key)[0])!!
set(value) = prefs.edit { putString(SOUND_SELECTION, value) }

val soundSelectionTitle: String
get() {
val keyArray = context.resources.getStringArray(R.array.sound_options_key)
val titleArray = context.resources.getStringArray(R.array.sound_options_title)
return titleArray[keyArray.indexOf(soundSelectionKey)]
}

companion object {
private const val MASTER_ENABLED = "master_enabled"
private const val SOUND_SELECTION = "sound_selection"
}
}
13 changes: 13 additions & 0 deletions app/src/main/res/drawable/ic_launcher_foreground.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="41.37931"
android:viewportHeight="41.37931"
android:tint="#313131">
<group android:translateX="8.689655"
android:translateY="8.689655">
<path
android:fillColor="#FF000000"
android:pathData="M12,3v10.55c-0.59,-0.34 -1.27,-0.55 -2,-0.55 -2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4V7h4V3h-6z"/>
</group>
</vector>
9 changes: 9 additions & 0 deletions app/src/main/res/drawable/ic_sound.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M12,3v10.55c-0.59,-0.34 -1.27,-0.55 -2,-0.55 -2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4V7h4V3h-6z"/>
</vector>
68 changes: 68 additions & 0 deletions app/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView 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:id="@+id/controlContainer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="72dp"
android:layout_marginEnd="72dp"
android:layout_gravity="center"
app:cardCornerRadius="8dp"
tools:context=".ui.MainActivity">

<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingStart="24dp"
android:paddingTop="16dp"
android:paddingEnd="24dp"
android:paddingBottom="24dp">

<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">

<TextView
android:id="@+id/textView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/boot_sound"
android:textAppearance="?textAppearanceBody1" />

<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/mainSwitch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="42dp" />

</LinearLayout>

<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="16dp"
android:background="?dividerHorizontal" />

<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
android:id="@+id/sound_option_dropdown_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/theme">

<AutoCompleteTextView
android:id="@+id/sound_option_dropdown"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:editable="false"
tools:ignore="LabelFor" />

</com.google.android.material.textfield.TextInputLayout>

</LinearLayout>

</com.google.android.material.card.MaterialCardView>
Loading

0 comments on commit 906d01a

Please sign in to comment.