Skip to content

Commit

Permalink
Initial check-in of UAMP in Kotlin.
Browse files Browse the repository at this point in the history
Change-Id: Ia8679b787dea3960e589a05cbe59123c236cb834
  • Loading branch information
nic0lette committed Dec 18, 2017
1 parent 8fcebf8 commit c074c1f
Show file tree
Hide file tree
Showing 45 changed files with 1,926 additions and 1 deletion.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
*.iml
.gradle
/local.properties
/.idea
.DS_Store
/build
*.iml
/captures
.externalNativeBuild
1 change: 1 addition & 0 deletions app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
55 changes: 55 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright 2017 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'

android {
compileSdkVersion 26
defaultConfig {
applicationId "com.example.android.uamp.next"
minSdkVersion 19
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}

dependencies {
implementation project(':media')

implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"

implementation 'com.android.support:appcompat-v7:26.1.0'
implementation 'com.android.support.constraint:constraint-layout:1.0.2'
implementation "android.arch.lifecycle:extensions:1.0.0"

implementation 'com.android.support:support-v4:26.1.0'
implementation 'com.android.support:recyclerview-v7:26.1.0'

testImplementation 'junit:junit:4.12'

androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
}
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
36 changes: 36 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright 2017 Google Inc. All rights reserved.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.android.uamp">

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

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

</manifest>
70 changes: 70 additions & 0 deletions app/src/main/java/com/example/android/uamp/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* Copyright 2017 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.example.android.uamp

import android.arch.lifecycle.ViewModelProviders
import android.os.Bundle
import android.support.v7.app.AppCompatActivity

class MainActivity : AppCompatActivity(), ConnectionCallback {

private lateinit var mediaBrowserConnection: MediaBrowserViewModel

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

mediaBrowserConnection = ViewModelProviders.of(this).get(MediaBrowserViewModel::class.java)
}

override fun onStart() {
super.onStart()
mediaBrowserConnection.registerCallback(this)
}

override fun onStop() {
super.onStop()
mediaBrowserConnection.unregisterCallback(this)
}

override fun onConnected() {
super.onConnected()

navigateToBrowser(mediaBrowserConnection.getRoot())
}

private fun navigateToBrowser(mediaId: String) {
var fragment: MediaItemFragment? = getBrowseFragment(mediaId)

if (fragment == null) {
fragment = MediaItemFragment.newInstance(mediaId)
val transaction = supportFragmentManager.beginTransaction()
transaction.replace(R.id.browse_fragment, fragment, mediaId)

// If this is not the top level media (root), we add it to the fragment back stack,
// so that actionbar toggle and Back will work appropriately:
if (mediaId != mediaBrowserConnection.getRoot()) {
transaction.addToBackStack(null)
}
transaction.commit()
}
}

private fun getBrowseFragment(mediaId: String): MediaItemFragment? {
return fragmentManager.findFragmentByTag(mediaId) as MediaItemFragment?
}
}
128 changes: 128 additions & 0 deletions app/src/main/java/com/example/android/uamp/MediaBrowserViewModel.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/*
* Copyright 2017 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.example.android.uamp

import android.app.Application
import android.arch.lifecycle.AndroidViewModel
import android.content.ComponentName
import android.support.annotation.NonNull
import android.support.v4.media.MediaBrowserCompat
import android.support.v4.media.session.MediaControllerCompat
import android.util.Log
import com.example.android.uamp.media.MusicService

/**
* ViewModel that implements (and holds onto) a MediaBrowser connection.
*/
class MediaBrowserViewModel(application: Application) : AndroidViewModel(application) {

private val mediaBrowser: MediaBrowserCompat
private val mediaBrowserConnectionCallback = MediaBrowserConnectionCallback()
private val mediaControllerCallback = MediaControllerCallback()

private lateinit var mediaController: MediaControllerCompat

private val callbacks = ArrayList<ConnectionCallback>()

init {
mediaBrowser = MediaBrowserCompat(
application,
ComponentName(application, MusicService::class.java),
mediaBrowserConnectionCallback,
null)
mediaBrowser.connect()
}

fun registerCallback(callback: ConnectionCallback) {
if (!callbacks.contains(callback)) {
callbacks.add(callback)

if (mediaBrowser.isConnected) {
callback.onConnected()
}
}
}

fun unregisterCallback(callback: ConnectionCallback) {
if (callbacks.contains(callback)) {
callbacks.remove(callback)
}
}

fun subscribe(parentId: String, callback: MediaBrowserCompat.SubscriptionCallback) {
mediaBrowser.subscribe(parentId, callback)
}

fun unsubscribe(parentId: String, callback: MediaBrowserCompat.SubscriptionCallback) {
mediaBrowser.unsubscribe(parentId, callback)
}

fun getRoot(): String {
return mediaBrowser.root
}

private inner class MediaBrowserConnectionCallback : MediaBrowserCompat.ConnectionCallback() {
override fun onConnected() {
super.onConnected()

// Get a MediaController for the MediaSession.
mediaController = MediaControllerCompat(getApplication(), mediaBrowser.sessionToken)
mediaController.registerCallback(mediaControllerCallback)

callbacks.forEach { callback -> callback.onConnected() }
}

override fun onConnectionSuspended() {
super.onConnectionSuspended()

callbacks.forEach { callback -> callback.onConnectionSuspended() }
}

override fun onConnectionFailed() {
super.onConnectionFailed()

callbacks.forEach { callback -> callback.onConnectionFailed() }
}
}

private inner class MediaControllerCallback : MediaControllerCompat.Callback() {
override fun onSessionDestroyed() {
super.onSessionDestroyed()

// Normally if a MediaBrowserService drops its connection the callback comes via
// MediaControllerCompat.Callback (here). But since other connection status events
// are sent to MediaBrowserCompat.ConnectionCallback, we catch the disconnect here
// and send it on to the other callback.
callbacks.forEach { callback -> callback.onConnectionSuspended() }
}
}
}

/**
* Interface to allow a class to receive callbacks based on the changing state of a
* [MediaBrowser] connection.
*/
interface ConnectionCallback {
fun onConnected() {
}

fun onConnectionSuspended() {
}

fun onConnectionFailed() {
}
}
Loading

0 comments on commit c074c1f

Please sign in to comment.