Skip to content

Commit

Permalink
Athena: Initial commit
Browse files Browse the repository at this point in the history
Change-Id: I57c8bdb7ed1a578b3e9c7567aabe2c8ab34d0916
  • Loading branch information
SebaUbuntu committed Feb 28, 2023
0 parents commit f87655d
Show file tree
Hide file tree
Showing 62 changed files with 2,581 additions and 0 deletions.
24 changes: 24 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: build

on: [push, pull_request, workflow_dispatch]

jobs:
build:
runs-on: ubuntu-latest

steps:
- name: Checkout repo
uses: actions/checkout@v1

- name: Setup JDK 11
uses: actions/setup-java@v1
with:
java-version: 11

- name: Build with Gradle
run: ./gradlew assembleDebug

- uses: actions/upload-artifact@v3
with:
name: app-debug.apk
path: app/build/outputs/apk/debug/app-debug.apk
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
*.iml
.gradle
/local.properties
/.idea
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties
1 change: 1 addition & 0 deletions app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
56 changes: 56 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
plugins {
id("com.android.application")
id("kotlin-android")
}

android {
namespace = "dev.sebaubuntu.athena"
compileSdk = 33

defaultConfig {
applicationId = "dev.sebaubuntu.athena"
minSdk = 23
targetSdk = 33
versionCode = 1
versionName = "1.0"
}

buildTypes {
getByName("release") {
// Enables code shrinking, obfuscation, and optimization.
isMinifyEnabled = true

// Enables resource shrinking.
isShrinkResources = true

// Includes the default ProGuard rules files.
setProguardFiles(
listOf(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
)
}
getByName("debug") {
// Append .dev to package name so we won't conflict with AOSP build.
applicationIdSuffix = ".dev"
}
}

compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}

kotlinOptions {
jvmTarget = "1.8"
}
}

dependencies {
implementation("androidx.core:core-ktx:1.9.0")
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation("androidx.viewpager2:viewpager2:1.1.0-beta01")
implementation("com.google.android.material:material:1.9.0-alpha02")
}
21 changes: 21 additions & 0 deletions app/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# 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
34 changes: 34 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
SPDX-FileCopyrightText: 2023 Sebastiano Barezzi
SPDX-License-Identifier: Apache-2.0
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission
android:name="android.permission.BLUETOOTH"
android:maxSdkVersion="30" />
<uses-permission
android:name="android.permission.BLUETOOTH_ADMIN"
android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />

<application
android:name=".AthenaApplication"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/Theme.Athena">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
17 changes: 17 additions & 0 deletions app/src/main/java/dev/sebaubuntu/athena/AthenaApplication.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* SPDX-FileCopyrightText: 2023 Sebastiano Barezzi
* SPDX-License-Identifier: Apache-2.0
*/

package dev.sebaubuntu.athena

import android.app.Application
import com.google.android.material.color.DynamicColors

class AthenaApplication : Application() {
override fun onCreate() {
super.onCreate()

DynamicColors.applyToActivitiesIfAvailable(this)
}
}
59 changes: 59 additions & 0 deletions app/src/main/java/dev/sebaubuntu/athena/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* SPDX-FileCopyrightText: 2023 Sebastiano Barezzi
* SPDX-License-Identifier: Apache-2.0
*/

package dev.sebaubuntu.athena

import android.os.Build
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.content.res.AppCompatResources
import androidx.fragment.app.FragmentActivity
import androidx.viewpager2.widget.ViewPager2
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayoutMediator
import dev.sebaubuntu.athena.ui.CategoriesPagerAdapter
import dev.sebaubuntu.athena.utils.Category
import dev.sebaubuntu.athena.utils.PermissionsUtils

class MainActivity : AppCompatActivity() {
// Views
private val tabLayout by lazy { findViewById<TabLayout>(R.id.tabLayout) }
private val viewPager2 by lazy { findViewById<ViewPager2>(R.id.viewPager2) }

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(findViewById(R.id.toolbar))

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
window.isNavigationBarContrastEnforced = true
}

viewPager2.adapter = CategoriesPagerAdapter(this)

TabLayoutMediator(tabLayout, viewPager2) { tab, position ->
val category = Category.categories[position]!!
tab.setText(category.name)
tab.icon = AppCompatResources.getDrawable(this, category.icon)
tab.setContentDescription(category.description)
}.attach()
}

@Deprecated("Deprecated in Java")
override fun onBackPressed() {
if (viewPager2.currentItem == 0) {
// If the user is currently looking at the first step, allow the system to handle
// the Back button. This calls finish() on this activity and pops the back stack.
super.onBackPressed()
} else {
// Otherwise, select the previous step.
viewPager2.currentItem = viewPager2.currentItem - 1
}
}

companion object {
private const val LOG_TAG = "Athena"
}
}
163 changes: 163 additions & 0 deletions app/src/main/java/dev/sebaubuntu/athena/categories/AudioCategory.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
/*
* SPDX-FileCopyrightText: 2023 Sebastiano Barezzi
* SPDX-License-Identifier: Apache-2.0
*/

package dev.sebaubuntu.athena.categories

import android.annotation.SuppressLint
import android.content.Context
import android.media.AudioDeviceInfo
import android.media.AudioManager
import android.os.Build
import dev.sebaubuntu.athena.R
import dev.sebaubuntu.athena.utils.Category

object AudioCategory : Category {
override val name = R.string.section_audio_name
override val description = R.string.section_audio_description
override val icon = R.drawable.ic_audio
override val requiredPermissions = arrayOf<String>()

@SuppressLint("HardwareIds")
override fun getInfo(context: Context) = mutableMapOf<String, Map<String, String>>().apply {
val audioManager = context.getSystemService(AudioManager::class.java)

this["General"] = mutableMapOf(
"Current audio mode" to when (audioManager.mode) {
AudioManager.MODE_NORMAL -> "Normal"
AudioManager.MODE_RINGTONE -> "Ringtone"
AudioManager.MODE_IN_CALL -> "In call"
AudioManager.MODE_IN_COMMUNICATION -> "In communication"
AudioManager.MODE_CALL_SCREENING -> "Call screening"
AudioManager.MODE_CALL_REDIRECT -> "Call redirect"
AudioManager.MODE_COMMUNICATION_REDIRECT -> "Communication redirect"
else -> "Unknown"
},
"Is volume fixed" to "${audioManager.isVolumeFixed}",
).apply {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
this["Is call screening mode supported"] = "${audioManager.isCallScreeningModeSupported}"
}
}

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S_V2) {
this["Spatializer"] = mapOf(
"Available" to "${audioManager.spatializer.isAvailable}",
"Enabled" to "${audioManager.spatializer.isEnabled}",
)
}

val outputDevices = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)
if (outputDevices.isNotEmpty()) {
this["Output devices"] = mapOf()
for (outputDevice in outputDevices) {
this["${outputDevice.id}"] = getAudioDeviceInfo(outputDevice)
}
}

val inputDevices = audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)
if (inputDevices.isNotEmpty()) {
this["Input devices"] = mapOf()
for (inputDevice in inputDevices) {
this["${inputDevice.id}"] = getAudioDeviceInfo(inputDevice)
}
}
}

private fun getAudioDeviceInfo(audioDeviceInfo: AudioDeviceInfo) =
mutableMapOf<String, String>().apply {
this["Device ID"] = audioDeviceInfo.id.toString()
this["Product name"] = audioDeviceInfo.productName.toString()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
val address = audioDeviceInfo.address
if (address.isNotEmpty()) {
this["Address"] = address
}
}

this["Audio device type"] = (deviceTypeToString[audioDeviceInfo.type] ?: "Unknown type")

this["Device type"] = when {
audioDeviceInfo.isSource && audioDeviceInfo.isSink -> "Source/sink"
audioDeviceInfo.isSource -> "Source"
audioDeviceInfo.isSink -> "Sink"
else -> "None"
}

val channelCounts = audioDeviceInfo.channelCounts
if (channelCounts.isNotEmpty()) {
this["Channel counts"] = channelCounts.joinToString()
}

val channelMasks = audioDeviceInfo.channelMasks
if (channelMasks.isNotEmpty()) {
this["Channel masks"] = channelMasks.joinToString()
}

val channelIndexMasks = audioDeviceInfo.channelIndexMasks
if (channelIndexMasks.isNotEmpty()) {
this["Channel index masks"] = channelIndexMasks.joinToString()
}

val sampleRates = audioDeviceInfo.sampleRates
if (sampleRates.isNotEmpty()) {
this["Sample rates"] = sampleRates.joinToString()
}

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
val audioDescriptors = audioDeviceInfo.audioDescriptors
if (audioDescriptors.isNotEmpty()) {
this["Audio descriptors"] = audioDescriptors.joinToString {
it.standard.toString()
}
}
}
}

private val deviceTypeToString = mutableMapOf(
AudioDeviceInfo.TYPE_UNKNOWN to "Unknown",
AudioDeviceInfo.TYPE_BUILTIN_EARPIECE to "Built in earpiece",
AudioDeviceInfo.TYPE_BUILTIN_SPEAKER to "Built in speaker",
AudioDeviceInfo.TYPE_WIRED_HEADSET to "Wired headset",
AudioDeviceInfo.TYPE_WIRED_HEADPHONES to "Wired headphones",
AudioDeviceInfo.TYPE_LINE_ANALOG to "Line analog",
AudioDeviceInfo.TYPE_LINE_DIGITAL to "Line digital",
AudioDeviceInfo.TYPE_BLUETOOTH_SCO to "Bluetooth SCO",
AudioDeviceInfo.TYPE_BLUETOOTH_A2DP to "Bluetooth A2DP",
AudioDeviceInfo.TYPE_HDMI to "HDMI",
AudioDeviceInfo.TYPE_HDMI_ARC to "HDMI ARC",
AudioDeviceInfo.TYPE_USB_DEVICE to "USB device",
AudioDeviceInfo.TYPE_USB_ACCESSORY to "USB accessory",
AudioDeviceInfo.TYPE_DOCK to "Dock",
AudioDeviceInfo.TYPE_FM to "FM",
AudioDeviceInfo.TYPE_BUILTIN_MIC to "Built in mic",
AudioDeviceInfo.TYPE_FM_TUNER to "FM tuner",
AudioDeviceInfo.TYPE_TV_TUNER to "TV tuner",
AudioDeviceInfo.TYPE_TELEPHONY to "Telephony",
AudioDeviceInfo.TYPE_AUX_LINE to "AUX line",
AudioDeviceInfo.TYPE_IP to "IP",
).apply {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
this[AudioDeviceInfo.TYPE_BUS] = "Bus"
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
this[AudioDeviceInfo.TYPE_USB_HEADSET] = "USB headset"
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
this[AudioDeviceInfo.TYPE_HEARING_AID] = "Hearing aid"
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
this[AudioDeviceInfo.TYPE_BUILTIN_SPEAKER_SAFE] = "Built in speaker safe"
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
this[AudioDeviceInfo.TYPE_REMOTE_SUBMIX] = "Remote submix"
this[AudioDeviceInfo.TYPE_BLE_HEADSET] = "BLE headset"
this[AudioDeviceInfo.TYPE_BLE_SPEAKER] = "BLE speaker"
this[AudioDeviceInfo.TYPE_HDMI_EARC] = "HDMI EARC"
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
this[AudioDeviceInfo.TYPE_BLE_BROADCAST] = "BLE broadcast"
}
}.toMap()
}
Loading

0 comments on commit f87655d

Please sign in to comment.