summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXin Li <delphij@google.com>2018-06-08 11:07:57 -0700
committerXin Li <delphij@google.com>2018-06-08 11:07:57 -0700
commit06c759ec0be685b06e25505002c7df7f8743e9c9 (patch)
tree4b825dc642cb6eb9a060e54bf8d69288fbee4904
parent1d3c08a433863cdd305ad3adb1a4964ec9ce766c (diff)
parentee1755b996310a3232f1fe9cc39a584d2e1b2aa2 (diff)
downloadStream-06c759ec0be685b06e25505002c7df7f8743e9c9.tar.gz
Merge pi-dev-plus-aosp-without-vendor into stage-aosp-mastertemp_p_merge
Bug: 79597307 Change-Id: I92458f6e195b0aa26c288db9521752e7b6f9b2d5
-rw-r--r--Android.mk67
-rw-r--r--AndroidManifest.xml80
-rw-r--r--res/drawable/ic_call_black.xml16
-rw-r--r--res/drawable/ic_call_missed.xml13
-rw-r--r--res/drawable/ic_mic.xml15
-rw-r--r--res/drawable/ic_mic_muted.xml18
-rw-r--r--res/drawable/ic_pause.xml13
-rw-r--r--res/drawable/ic_pause_light.xml13
-rw-r--r--res/drawable/ic_phone.xml10
-rw-r--r--res/drawable/ic_phone_hangup.xml9
-rw-r--r--res/drawable/ic_play_arrow.xml15
-rw-r--r--res/drawable/ic_play_arrow_light.xml15
-rw-r--r--res/drawable/ic_skip_next.xml12
-rw-r--r--res/drawable/ic_skip_previous.xml12
-rw-r--r--res/drawable/ic_stop.xml13
-rw-r--r--res/values/colors.xml19
-rw-r--r--res/values/dimens.xml6
-rw-r--r--res/values/strings.xml57
-rw-r--r--src/com/android/car/stream/BitmapUtils.java48
-rw-r--r--src/com/android/car/stream/PermissionsActivity.java171
-rw-r--r--src/com/android/car/stream/StreamApplication.java83
-rw-r--r--src/com/android/car/stream/StreamProducer.java148
-rw-r--r--src/com/android/car/stream/StreamService.java197
-rw-r--r--src/com/android/car/stream/StreamServiceConstants.java50
-rw-r--r--src/com/android/car/stream/media/MediaAppInfo.java161
-rw-r--r--src/com/android/car/stream/media/MediaConverter.java143
-rw-r--r--src/com/android/car/stream/media/MediaPlaybackMonitor.java342
-rw-r--r--src/com/android/car/stream/media/MediaStateManager.java248
-rw-r--r--src/com/android/car/stream/media/MediaStreamProducer.java238
-rw-r--r--src/com/android/car/stream/media/MediaUtils.java66
-rw-r--r--src/com/android/car/stream/notifications/StreamNotificationListenerService.java41
-rw-r--r--src/com/android/car/stream/radio/RadioConverter.java164
-rw-r--r--src/com/android/car/stream/radio/RadioFormatter.java93
-rw-r--r--src/com/android/car/stream/radio/RadioStreamProducer.java326
-rw-r--r--src/com/android/car/stream/telecom/CurrentCallConverter.java126
-rw-r--r--src/com/android/car/stream/telecom/CurrentCallStreamProducer.java234
-rw-r--r--src/com/android/car/stream/telecom/RecentCallConverter.java59
-rw-r--r--src/com/android/car/stream/telecom/RecentCallStreamProducer.java135
-rw-r--r--src/com/android/car/stream/telecom/StreamInCallService.java100
-rw-r--r--src/com/android/car/stream/telecom/TelecomConstants.java28
-rw-r--r--src/com/android/car/stream/telecom/TelecomUtils.java359
41 files changed, 0 insertions, 3963 deletions
diff --git a/Android.mk b/Android.mk
deleted file mode 100644
index 7700171..0000000
--- a/Android.mk
+++ /dev/null
@@ -1,67 +0,0 @@
-#
-# Copyright (C) 2016 The Android Open Source Project
-#
-# 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.
-#
-ifneq ($(TARGET_BUILD_PDK), true)
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-
-include packages/apps/Car/libs/car-stream-ui-lib/car-stream-ui-lib.mk
-include packages/apps/Car/libs/car-apps-common/car-apps-common.mk
-
-include packages/services/Car/car-support-lib/car-support.mk
-
-LOCAL_USE_AAPT2 := true
-
-LOCAL_STATIC_ANDROID_LIBRARIES += \
- android-support-v4 \
- car-radio-service
-
-LOCAL_STATIC_JAVA_LIBRARIES += \
- car-stream-lib
-
-LOCAL_PACKAGE_NAME := Stream
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-LOCAL_MODULE_TAGS := optional
-
-#TODO: determine if this service should be a privileged module.
-LOCAL_PRIVILEGED_MODULE := true
-
-LOCAL_PROGUARD_ENABLED := disabled
-
-LOCAL_DEX_PREOPT := false
-
-# Include support-v7-cardview, if not already included
-ifeq (,$(findstring android-support-v7-cardview,$(LOCAL_STATIC_ANDROID_LIBRARIES)))
-LOCAL_STATIC_ANDROID_LIBRARIES += android-support-v7-cardview
-endif
-
-# Include support-v7-palette, if not already included
-ifeq (,$(findstring android-support-v7-palette,$(LOCAL_STATIC_ANDROID_LIBRARIES)))
-LOCAL_STATIC_ANDROID_LIBRARIES += android-support-v7-palette
-endif
-
-# Include android-support-annotations, if not already included
-ifeq (,$(findstring android-support-annotations,$(LOCAL_STATIC_JAVA_LIBRARIES)))
-LOCAL_STATIC_JAVA_LIBRARIES += android-support-annotations
-endif
-
-include $(BUILD_PACKAGE)
-
-endif
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
deleted file mode 100644
index 8bc0317..0000000
--- a/AndroidManifest.xml
+++ /dev/null
@@ -1,80 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2016 The Android Open Source Project
-
- 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.android.car.stream">
- <uses-sdk
- android:minSdkVersion="23"
- android:targetSdkVersion='23'/>
- <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
- <uses-permission android:name="android.permission.READ_CONTACTS" />
- <uses-permission android:name="android.permission.WRITE_CONTACTS" />
- <uses-permission android:name="android.permission.CALL_PHONE" />
- <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
- <uses-permission android:name="android.permission.READ_CALL_LOG"/>
- <uses-permission android:name="android.permission.WAKE_LOCK"/>
- <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
- <uses-permission android:name="android.permission.INTERNET" />
- <uses-permission android:name="android.permission.RECEIVE_SMS" />
- <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
- <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
- <uses-permission android:name="com.google.android.car.LAUNCH_PROJECTION_APP" />
- <uses-permission android:name="android.permission.GET_ACCOUNTS"/>
- <uses-permission android:name="android.permission.CONTROL_INCALL_EXPERIENCE"/>
- <uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL"/>
- <uses-permission android:name="android.permission.MANAGE_USERS" />
- <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
-
- <application android:label="CarStreamService"
- android:name="com.android.car.stream.StreamApplication"
- android:persistent="true">
- <activity android:name="com.android.car.stream.PermissionsActivity"
- android:theme="@android:style/Theme.NoTitleBar"
- android:resizeableActivity="true"
- android:launchMode="singleTask"
- android:label="StreamPermissionActivity">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
- </activity>
- <service
- android:name="com.android.car.stream.StreamService"
- android:exported="true">
- <intent-filter>
- <action android:name="stream.service"/>
- </intent-filter>
- </service>
-
- <service android:name="com.android.car.stream.notifications.StreamNotificationListenerService"
- android:label="Stream Notification Listener"
- android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
- <intent-filter>
- <action android:name="android.service.notification.NotificationListenerService" />
- </intent-filter>
- </service>
-
- <service android:name="com.android.car.stream.telecom.StreamInCallService"
- android:permission="android.permission.BIND_INCALL_SERVICE"
- android:exported="true">
- <meta-data android:name="android.telecom.IN_CALL_SERVICE_UI" android:value="false" />
- <intent-filter>
- <action android:name="android.telecom.InCallService"/>
- </intent-filter>
- </service>
- </application>
-</manifest>
diff --git a/res/drawable/ic_call_black.xml b/res/drawable/ic_call_black.xml
deleted file mode 100644
index 4a34968..0000000
--- a/res/drawable/ic_call_black.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
- android:viewportWidth="48"
- android:viewportHeight="48">
-
- <path
- android:pathData="M0 0h48v48H0z" />
- <path
- android:fillColor="#000000"
- android:pathData="M13.25 21.59c2.88 5.66 7.51 10.29 13.18 13.17l4.4-4.41c.55-.55 1.34-.71
-2.03-.49C35.1 30.6 37.51 31 40 31c1.11 0 2 .89 2 2v7c0 1.11-.89 2-2 2C21.22 42 6
-26.78 6 8c0-1.11 .9 -2 2-2h7c1.11 0 2 .89 2 2 0 2.49 .4 4.9 1.14 7.14 .22 .69
-.06 1.48-.49 2.03l-4.4 4.42z" />
-</vector>
diff --git a/res/drawable/ic_call_missed.xml b/res/drawable/ic_call_missed.xml
deleted file mode 100644
index b2e065e..0000000
--- a/res/drawable/ic_call_missed.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24"
- android:viewportHeight="24">
-
- <path
- android:pathData="M0 0h24v24H0z" />
- <path
- android:fillColor="@color/car_red_500"
- android:pathData="M19.59 7L12 14.59 6.41 9H11V7H3v8h2v-4.59l7 7 9-9z" />
-</vector>
diff --git a/res/drawable/ic_mic.xml b/res/drawable/ic_mic.xml
deleted file mode 100644
index 12e451a..0000000
--- a/res/drawable/ic_mic.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
- android:viewportWidth="48"
- android:viewportHeight="48">
-
- <path
- android:fillColor="#FFFFFF"
- android:pathData="M24 28c3.31 0 5.98-2.69 5.98-6L30 10c0-3.32-2.68-6-6-6-3.31 0-6 2.68-6 6v12c0
-3.31 2.69 6 6 6zm10.6-6c0 6-5.07 10.2-10.6 10.2-5.52 0-10.6-4.2-10.6-10.2H10c0
-6.83 5.44 12.47 12 13.44V42h4v-6.56c6.56-.97 12-6.61 12-13.44h-3.4z" />
- <path
- android:pathData="M0 0h48v48H0z" />
-</vector> \ No newline at end of file
diff --git a/res/drawable/ic_mic_muted.xml b/res/drawable/ic_mic_muted.xml
deleted file mode 100644
index b46a00a..0000000
--- a/res/drawable/ic_mic_muted.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
- android:viewportWidth="48"
- android:viewportHeight="48">
-
- <path
- android:pathData="M0 0h48v48H0zm0 0h48v48H0z" />
- <path
- android:fillColor="#FFFFFF"
- android:pathData="M38 22h-3.4c0 1.49-.31 2.87-.87 4.1l2.46 2.46C37.33 26.61 38 24.38 38 22zm-8.03
-.33 c0-.11 .03 -.22 .03 -.33V10c0-3.32-2.69-6-6-6s-6 2.68-6 6v.37l11.97
-11.96zM8.55 6L6 8.55l12.02 12.02v1.44c0 3.31 2.67 6 5.98 6 .45 0 .88-.06
-1.3-.15l3.32 3.32c-1.43 .66 -3 1.03-4.62 1.03-5.52 0-10.6-4.2-10.6-10.2H10c0
-6.83 5.44 12.47 12 13.44V42h4v-6.56c1.81-.27 3.53-.9 5.08-1.81L39.45 42 42 39.46
-8.55 6z" />
-</vector> \ No newline at end of file
diff --git a/res/drawable/ic_pause.xml b/res/drawable/ic_pause.xml
deleted file mode 100644
index 638e987..0000000
--- a/res/drawable/ic_pause.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
- android:viewportWidth="48"
- android:viewportHeight="48">
-
- <path
- android:fillColor="#000000"
- android:pathData="M12 38h8V10h-8v28zm16-28v28h8V10h-8z" />
- <path
- android:pathData="M0 0h48v48H0z" />
-</vector> \ No newline at end of file
diff --git a/res/drawable/ic_pause_light.xml b/res/drawable/ic_pause_light.xml
deleted file mode 100644
index 11784f3..0000000
--- a/res/drawable/ic_pause_light.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
- android:viewportWidth="48"
- android:viewportHeight="48">
-
- <path
- android:fillColor="#FFFFFF"
- android:pathData="M12 38h8V10h-8v28zm16-28v28h8V10h-8z" />
- <path
- android:pathData="M0 0h48v48H0z" />
-</vector>
diff --git a/res/drawable/ic_phone.xml b/res/drawable/ic_phone.xml
deleted file mode 100644
index 015338f..0000000
--- a/res/drawable/ic_phone.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="56dp"
- android:height="56dp"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0">
- <path
- android:pathData="M6.62,10.79c1.44,2.83 3.76,5.14 6.59,6.59l2.2,-2.2c0.27,-0.27 0.67,-0.36 1.02,-0.24 1.12,0.37 2.33,0.57 3.57,0.57 0.55,0 1,0.45 1,1V20c0,0.55 -0.45,1 -1,1 -9.39,0 -17,-7.61 -17,-17 0,-0.55 0.45,-1 1,-1h3.5c0.55,0 1,0.45 1,1 0,1.25 0.2,2.45 0.57,3.57 0.11,0.35 0.03,0.74 -0.25,1.02l-2.2,2.2z"
- android:fillColor="#000000"/>
-</vector>
diff --git a/res/drawable/ic_phone_hangup.xml b/res/drawable/ic_phone_hangup.xml
deleted file mode 100644
index 7af35f1..0000000
--- a/res/drawable/ic_phone_hangup.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<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:pathData="M12,9c-1.6,0 -3.15,0.25 -4.6,0.72v3.1c0,0.39 -0.23,0.74 -0.56,0.9 -0.98,0.49 -1.87,1.12 -2.66,1.85 -0.18,0.18 -0.43,0.28 -0.7,0.28 -0.28,0 -0.53,-0.11 -0.71,-0.29L0.29,13.08c-0.18,-0.17 -0.29,-0.42 -0.29,-0.7 0,-0.28 0.11,-0.53 0.29,-0.71C3.34,8.78 7.46,7 12,7s8.66,1.78 11.71,4.67c0.18,0.18 0.29,0.43 0.29,0.71 0,0.28 -0.11,0.53 -0.29,0.71l-2.48,2.48c-0.18,0.18 -0.43,0.29 -0.71,0.29 -0.27,0 -0.52,-0.11 -0.7,-0.28 -0.79,-0.74 -1.69,-1.36 -2.67,-1.85 -0.33,-0.16 -0.56,-0.5 -0.56,-0.9v-3.1C15.15,9.25 13.6,9 12,9z"
- android:fillColor="#ffffff"/>
-</vector> \ No newline at end of file
diff --git a/res/drawable/ic_play_arrow.xml b/res/drawable/ic_play_arrow.xml
deleted file mode 100644
index 753afe7..0000000
--- a/res/drawable/ic_play_arrow.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
- android:viewportWidth="48"
- android:viewportHeight="48">
-
- <path
- android:pathData="M-838-2232H562v3600H-838z" />
- <path
- android:fillColor="#000000"
- android:pathData="M16 10v28l22-14z" />
- <path
- android:pathData="M0 0h48v48H0z" />
-</vector> \ No newline at end of file
diff --git a/res/drawable/ic_play_arrow_light.xml b/res/drawable/ic_play_arrow_light.xml
deleted file mode 100644
index 41ec9ef..0000000
--- a/res/drawable/ic_play_arrow_light.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
- android:viewportWidth="48"
- android:viewportHeight="48">
-
- <path
- android:pathData="M-838-2232H562v3600H-838z" />
- <path
- android:fillColor="#FFFFFF"
- android:pathData="M16 10v28l22-14z" />
- <path
- android:pathData="M0 0h48v48H0z" />
-</vector>
diff --git a/res/drawable/ic_skip_next.xml b/res/drawable/ic_skip_next.xml
deleted file mode 100644
index 29334a3..0000000
--- a/res/drawable/ic_skip_next.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
- android:viewportWidth="48"
- android:viewportHeight="48">
- <path
- android:fillColor="#FFFFFF"
- android:pathData="M12 36l17-12-17-12v24zm20-24v24h4V12h-4z" />
- <path
- android:pathData="M0 0h48v48H0z" />
-</vector> \ No newline at end of file
diff --git a/res/drawable/ic_skip_previous.xml b/res/drawable/ic_skip_previous.xml
deleted file mode 100644
index 0a19a6f..0000000
--- a/res/drawable/ic_skip_previous.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
- android:viewportWidth="48"
- android:viewportHeight="48">
- <path
- android:fillColor="#FFFFFF"
- android:pathData="M12 12h4v24h-4zm7 12l17 12V12z" />
- <path
- android:pathData="M0 0h48v48H0z" />
-</vector> \ No newline at end of file
diff --git a/res/drawable/ic_stop.xml b/res/drawable/ic_stop.xml
deleted file mode 100644
index 105e269..0000000
--- a/res/drawable/ic_stop.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
- android:viewportWidth="48"
- android:viewportHeight="48">
-
- <path
- android:pathData="M0 0h48v48H0z" />
- <path
- android:fillColor="#000000"
- android:pathData="M12 12h24v24H12z" />
-</vector> \ No newline at end of file
diff --git a/res/values/colors.xml b/res/values/colors.xml
deleted file mode 100644
index 415049a..0000000
--- a/res/values/colors.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
-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.
--->
-<resources>
- <!-- The main accent color of the radio app. -->
- <color name="car_radio_accent_color">#e91e63</color> <!-- Pink 500 -->
-</resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
deleted file mode 100644
index 936f912..0000000
--- a/res/values/dimens.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2016 Google Inc. All Rights Reserved. -->
-<resources>
- <dimen name="stream_card_secondary_icon_dimen">96dp</dimen>
- <dimen name="stream_media_icon_size">128dp</dimen>
-</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
deleted file mode 100644
index 751fabf..0000000
--- a/res/values/strings.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2016 Google Inc. All Rights Reserved. -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
- <!-- Permission -->
- <skip/>
- <!-- Generic instruction on how to enable permissions for Android Auto [CHAR LIMIT=200] -->
- <string name="permissions_generic">Use your phone to turn on the permissions in\nSettings > Apps > Android Auto > Permissions</string>
-
- <string name="permission_not_granted">The following permissions where not granted:<xliff:g id="temperature">%1$s</xliff:g></string>
- <string name="all_permission_granted">All permissions granted, starting service</string>
- <string name="permission_dialog_title">Permissions</string>
- <string name="permission_dialog_positive_button_text">Ok</string>
-
- <!-- Label for a recent call card [CHAR LIMIT=30] -->
- <string name="recent_call">Recent call</string>
-
- <string name="car_notification_permission_dialog_title">Notification access request</string>
- <string name="car_notification_permission_dialog_text">Please enable notification access and then hit the home button</string>
-
- <!-- Telecom Related strings-->
- <!-- Label for voicemail [CHAR LIMIT=30] -->
- <string name="voicemail">Voicemail</string>
- <!-- Label for current phone call [CHAR LIMIT=30] -->
- <string name="unknown_number">Current call</string>
- <!-- Label for incoming call [CHAR LIMIT=30] -->
- <string name="notification_incoming_call">Select to answer</string>
- <!-- Label for button to answer a phone call [CHAR LIMIT=30] -->
- <string name="answer_call">Answer</string>
- <!-- Label for button to reject a phone call [CHAR LIMIT=30] -->
- <string name="reject_call">Reject</string>
- <!-- Label for when a call is coming from an unknown caller [CHAR LIMIT=30] -->
- <string name="unknown">Unknown</string>
- <!-- Label for when a call is a conference call [CHAR LIMIT=30] -->
- <string name="conference_call">Conference call</string>
- <!-- Label for the currently ongoing call [CHAR LIMIT=30] -->
- <string name="ongoing_call">Active &#8226; </string>
- <!-- Label for the currently dialed call [CHAR LIMIT=30] -->
- <string name="dialing_call">Dialing</string>
- <!-- Label for a call being disconnected [CHAR LIMIT=30] -->
- <string name="disconnecting_call">Disconnecting Call</string>
-
- <!-- Text for the radio application. -->
- <string name="radio_app_name">Radio</string>
-
- <!-- Text to denote the AM radio band. -->
- <string name="radio_am_text">AM</string>
-
- <!-- Text to denote the FM radio band. -->
- <string name="radio_fm_text">FM</string>
-
- <string name="car_media_component_package" translatable="false">com.android.car.media</string>
-
- <string name="car_radio_component_package" translatable="false">com.android.car.radio</string>
- <string name="car_radio_component_service" translatable="false">com.android.car.radio.RadioService</string>
- <string name="car_radio_component_activity" translatable="false">com.android.car.radio.CarRadioProxyActivity</string>
-</resources>
diff --git a/src/com/android/car/stream/BitmapUtils.java b/src/com/android/car/stream/BitmapUtils.java
deleted file mode 100644
index e68b2ae..0000000
--- a/src/com/android/car/stream/BitmapUtils.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
- *
- * 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.android.car.stream;
-
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.drawable.VectorDrawable;
-import android.support.annotation.Nullable;
-
-/**
- * Utility class for manipulating Bitmaps.
- */
-public class BitmapUtils {
- private BitmapUtils() {}
-
- /**
- * Returns a {@link Bitmap} from a {@link VectorDrawable}.
- * {@link android.graphics.BitmapFactory#decodeResource(Resources, int)} cannot be used to
- * retrieve a bitmap from a VectorDrawable, so this method works around that.
- */
- @Nullable
- public static Bitmap getBitmap(VectorDrawable vectorDrawable) {
- if (vectorDrawable == null) {
- return null;
- }
-
- Bitmap bitmap = Bitmap.createBitmap(vectorDrawable.getIntrinsicWidth(),
- vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(bitmap);
- vectorDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
- vectorDrawable.draw(canvas);
- return bitmap;
- }
-}
diff --git a/src/com/android/car/stream/PermissionsActivity.java b/src/com/android/car/stream/PermissionsActivity.java
deleted file mode 100644
index 16e7719..0000000
--- a/src/com/android/car/stream/PermissionsActivity.java
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
- *
- * 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.android.car.stream;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.content.ComponentName;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.os.Bundle;
-import android.provider.Settings;
-import android.util.Log;
-import com.android.car.stream.notifications.StreamNotificationListenerService;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * A trampoline activity that checks if all permissions necessary are granted.
- */
-public class PermissionsActivity extends Activity {
- private static final String TAG = "PermissionsActivity";
- private static final String NOTIFICATION_LISTENER_ENABLED = "enabled_notification_listeners";
-
- public static final int CAR_PERMISSION_REQUEST_CODE = 1013; // choose a unique number
-
- private static final String[] PERMISSIONS = new String[]{
- android.Manifest.permission.READ_PHONE_STATE,
- android.Manifest.permission.CALL_PHONE,
- android.Manifest.permission.READ_CALL_LOG,
- android.Manifest.permission.READ_CONTACTS,
- android.Manifest.permission.ACCESS_FINE_LOCATION,
- android.Manifest.permission.RECEIVE_SMS,
- android.Manifest.permission.READ_EXTERNAL_STORAGE
- };
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- boolean permissionsCheckOnly = getIntent().getExtras()
- .getBoolean(StreamConstants.STREAM_PERMISSION_CHECK_PERMISSIONS_ONLY);
-
- if (permissionsCheckOnly) {
- boolean allPermissionsGranted = hasNotificationListenerPermission()
- && arePermissionGranted(PERMISSIONS);
- setResult(allPermissionsGranted ? RESULT_OK : RESULT_CANCELED);
- finish();
- return;
- }
-
- if (!hasNotificationListenerPermission()) {
- showNotificationListenerSettings();
- } else {
- maybeRequestPermissions();
- }
- }
-
- private void maybeRequestPermissions() {
- boolean permissionGranted = arePermissionGranted(PERMISSIONS);
- if (!permissionGranted) {
- requestPermissions(PERMISSIONS, CAR_PERMISSION_REQUEST_CODE);
- } else {
- startService(new Intent(this, StreamService.class));
- finish();
- }
- }
-
- @Override
- public void onRequestPermissionsResult(int requestCode, String[] permissions,
- int[] grantResults) {
- super.onRequestPermissionsResult(requestCode, permissions, grantResults);
-
- if (requestCode == CAR_PERMISSION_REQUEST_CODE) {
- List<String> granted = new ArrayList<>();
- List<String> notGranted = new ArrayList<>();
- for (int i = 0; i < permissions.length; i++) {
- String permission = permissions[i];
- int grantResult = grantResults[i];
- if (grantResult == PackageManager.PERMISSION_GRANTED) {
- granted.add(permission);
- } else {
- notGranted.add(permission);
- }
- }
-
- if (notGranted.size() > 0) {
- StringBuilder stb = new StringBuilder();
- for (String s : notGranted) {
- stb.append(" ").append(s);
- }
- showDialog(getString(R.string.permission_not_granted, stb.toString()));
- } else {
- showDialog(getString(R.string.all_permission_granted));
- startService(new Intent(this, StreamService.class));
- }
-
- if (arePermissionGranted(PERMISSIONS)) {
- setResult(Activity.RESULT_OK);
- }
- finish();
- }
- }
-
- private void showDialog(String message) {
- new AlertDialog.Builder(this /* context */)
- .setTitle(getString(R.string.permission_dialog_title))
- .setMessage(message)
- .setIcon(android.R.drawable.ic_dialog_alert)
- .setPositiveButton(
- getString(R.string.permission_dialog_positive_button_text),
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- dialog.cancel();
- }
- })
- .show();
- }
-
- private boolean arePermissionGranted(String[] permissions) {
- for (String permission : permissions) {
- if (checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
- Log.e(TAG, "Permission is not granted: " + permission);
- return false;
- }
- }
- return true;
- }
-
- private boolean hasNotificationListenerPermission() {
- ComponentName notificationListener = new ComponentName(this,
- StreamNotificationListenerService.class);
- String listeners = Settings.Secure.getString(getContentResolver(),
- NOTIFICATION_LISTENER_ENABLED);
- return listeners != null && listeners.contains(notificationListener.flattenToString());
- }
-
- private void showNotificationListenerSettings() {
- AlertDialog dialog = new AlertDialog.Builder(this)
- .setTitle(getString(R.string.car_notification_permission_dialog_title))
- .setMessage(getString(R.string.car_notification_permission_dialog_text))
- .setCancelable(false)
- .setNeutralButton(getString(android.R.string.ok),
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int id) {
- dialog.cancel();
- Intent settingsIntent =
- new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS);
- startActivity(settingsIntent);
- }
- })
- .create();
- dialog.show();
- }
-}
diff --git a/src/com/android/car/stream/StreamApplication.java b/src/com/android/car/stream/StreamApplication.java
deleted file mode 100644
index e01e2e7..0000000
--- a/src/com/android/car/stream/StreamApplication.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
- *
- * 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.android.car.stream;
-
-import android.app.Application;
-import android.content.Intent;
-import android.util.Log;
-import com.android.car.stream.media.MediaStreamProducer;
-import com.android.car.stream.radio.RadioStreamProducer;
-import com.android.car.stream.telecom.CurrentCallStreamProducer;
-import com.android.car.stream.telecom.RecentCallStreamProducer;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Base application for {@link StreamService}
- */
-public class StreamApplication extends Application {
- private static final String TAG = "StreamApplication";
- private List<StreamProducer> streamProducers;
-
- @Override
- public void onCreate() {
- // TODO(victorchan): start and bind stream service, then pass in bound instance to
- // producers.
- startService(new Intent(this, StreamService.class));
-
- super.onCreate();
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "stream application started");
- }
- streamProducers = new ArrayList<>();
- streamProducers.add(new CurrentCallStreamProducer(this /* context */));
- streamProducers.add(new RecentCallStreamProducer(this /* context */));
- streamProducers.add(new MediaStreamProducer(this /* context */));
- streamProducers.add(new RadioStreamProducer(this /* context */));
-
- startProducers();
- }
-
- @Override
- public void onTerminate() {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "StreamApplication terminated");
- }
- super.onTerminate();
- stopProducers();
- }
-
- private void startProducers() {
- for (int i = 0; i < streamProducers.size(); i++) {
- streamProducers.get(i).start();
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Stream producers started: "
- + streamProducers.get(i).getClass().getName());
- }
- }
- }
-
- private void stopProducers() {
- for (int i = 0; i < streamProducers.size(); i++) {
- streamProducers.get(i).stop();
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Stream producers stopped: "
- + streamProducers.get(i).getClass().getName());
- }
- }
- }
-}
diff --git a/src/com/android/car/stream/StreamProducer.java b/src/com/android/car/stream/StreamProducer.java
deleted file mode 100644
index d4971fc..0000000
--- a/src/com/android/car/stream/StreamProducer.java
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
- *
- * 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.android.car.stream;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.os.IBinder;
-import android.support.annotation.CallSuper;
-import android.util.Log;
-
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.Queue;
-
-/**
- * A base class that produces {@link StreamCard} for the StreamService
- */
-public abstract class StreamProducer {
- private static final String TAG = "StreamProducer";
-
- protected final Context mContext;
-
- /**
- * A queue that holds {@link StreamCard}s that were added before this {@link StreamProducer}
- * has connected to the {@link StreamService}. After connecting, these cards are posted to
- * the StreamService.
- */
- private final Queue<StreamCard> mQueuedCards = new LinkedList<>();
-
- private StreamService mStreamService;
-
- public StreamProducer(Context context) {
- mContext = context;
- }
-
- /**
- * Posts the given card to the {@link StreamService} for rendering by stream consumers.
- *
- * @return {@code true} if the card was successfully posted. {@code false} is returned if the
- * {@link StreamService} is not available. The given card will be queued and posted when the
- * {@link StreamService} becomes available.
- */
- public final boolean postCard(StreamCard card) {
- if (mStreamService != null) {
- mStreamService.addStreamCard(card);
- return true;
- }
-
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "StreamService not found, adding card to queue for later addition.");
- }
-
- mQueuedCards.add(card);
- return false;
- }
-
- /**
- * Removes the given card from the {@link StreamService}. If this {@link StreamProducer} has not
- * connected to the {@link StreamService}, then {@link #mQueuedCards} is checked to see if it
- * contains the given card.
- *
- * @return {@code true} if the card is successfully removed from either the
- * {@link StreamService} or {@link #mQueuedCards}.
- */
- public final boolean removeCard(StreamCard card) {
- if (card == null) {
- return false;
- }
-
- if (mStreamService != null) {
- mStreamService.removeStreamCard(card);
- return true;
- }
-
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "StreamService not found, checking if it exists in the queue.");
- }
-
- for (Iterator<StreamCard> iterator = mQueuedCards.iterator(); iterator.hasNext();) {
- StreamCard queuedCard = iterator.next();
- if (queuedCard.getType() == card.getType() && queuedCard.getId() == card.getId()) {
- iterator.remove();
- return true;
- }
- }
-
- return false;
- }
-
- public void onCardDismissed(StreamCard card) {
- // Handle when a StreamCard is dismissed.
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Stream Card dismissed: " + card);
- }
- }
-
- /**
- * Start the producer and connect to the {@link StreamService}
- */
- @CallSuper
- public void start() {
- Intent streamServiceIntent = new Intent(mContext, StreamService.class);
- streamServiceIntent.setAction(StreamConstants.STREAM_PRODUCER_BIND_ACTION);
- mContext.bindService(streamServiceIntent, mServiceConnection, 0 /* flags */);
- }
-
- /**
- * Stop the producer.
- */
- @CallSuper
- public void stop() {
- mContext.unbindService(mServiceConnection);
- mQueuedCards.clear();
- }
-
- private ServiceConnection mServiceConnection = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- StreamService.StreamProducerBinder binder
- = (StreamService.StreamProducerBinder) service;
- mStreamService = binder.getService();
-
- while (!mQueuedCards.isEmpty()) {
- mStreamService.addStreamCard(mQueuedCards.remove());
- }
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- mStreamService = null;
- }
- };
-}
diff --git a/src/com/android/car/stream/StreamService.java b/src/com/android/car/stream/StreamService.java
deleted file mode 100644
index 75a2c38..0000000
--- a/src/com/android/car/stream/StreamService.java
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
- *
- * 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.android.car.stream;
-
-import android.app.Service;
-import android.content.Intent;
-import android.os.Binder;
-import android.os.DeadObjectException;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Log;
-import android.util.Pair;
-
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.List;
-
-/**
- * A service that manages the {@link StreamCard} being generated by the system and notifies
- * the {@link IStreamConsumer} that new cards are available.
- */
-public class StreamService extends Service {
- private static final String TAG = "StreamService";
- private static final int DEFAULT_STREAM_CONSUMER_COUNT = 3;
-
- // The StreamCard is identified by a key which is comprised of its type and id
- private LinkedHashMap<Pair<Integer, Long>, StreamCard> mStreamCards = new LinkedHashMap<>();
-
- private List<IStreamConsumer> mConsumers = new ArrayList<>(DEFAULT_STREAM_CONSUMER_COUNT);
-
- private final IBinder mStreamProducerBinder = new StreamProducerBinder();
-
-
- public class StreamProducerBinder extends Binder {
- StreamService getService() {
- return StreamService.this;
- }
- }
-
- @Override
- public IBinder onBind(Intent intent) {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "onBind() calling process ID: " + Binder.getCallingPid()
- + " StreamService process ID: " + android.os.Process.myPid());
- }
-
- String action = intent.getAction();
- switch(action){
- case StreamConstants.STREAM_PRODUCER_BIND_ACTION:
- return mStreamProducerBinder;
- case StreamConstants.STREAM_CONSUMER_BIND_ACTION:
- return mStreamConsumerService;
- default:
- return null;
- }
- }
-
- private final IBinder mStreamConsumerService = new IStreamService.Stub() {
- @Override
- public void registerConsumer(IStreamConsumer consumer) throws RemoteException {
- mConsumers.add(consumer);
-
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Consumer registered, total # consumers: " + mConsumers.size());
- }
- }
-
- @Override
- public void unregisterConsumer(IStreamConsumer consumer) throws RemoteException {
- mConsumers.remove(consumer);
-
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Consumer removed, total # consumers: " + mConsumers.size());
- }
- }
-
- @Override
- public List<StreamCard> fetchAllStreamCards() throws RemoteException {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Fetching all stream items, # cards: " + mStreamCards.size());
- }
-
- List<StreamCard> cards = new ArrayList(mStreamCards.values());
- return cards;
- }
-
- @Override
- public void notifyStreamCardDismissed(StreamCard card) throws RemoteException {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "StreamCard dismissed");
- }
- }
-
- @Override
- public void notifyStreamCardInteracted(StreamCard card) throws RemoteException {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "StreamCard clicked");
- }
- }
- };
-
- /**
- * Add a {@link StreamCard} to the StreamService. The {@link StreamCard} will be published to
- * all IStreamListener registered with the StreamService.
- */
- public void addStreamCard(StreamCard card) {
- if (card == null) {
- return;
- }
- rankStreamCard(card);
- mStreamCards.put(getStreamCardKey(card), card);
- notifyListenersCardAdded(card);
- }
-
- /**
- * Remove a {@link StreamCard} to the StreamService. All registered {@link IStreamConsumer} will
- * be notified of the removal.
- *
- * @param card
- */
- public void removeStreamCard(StreamCard card) {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Stream Card Removed: " + card.toString());
- }
-
- if (card == null) {
- return;
- }
-
- mStreamCards.remove(getStreamCardKey(card));
- notifyListenersCardRemoved(card);
- }
-
- private Pair<Integer, Long> getStreamCardKey(StreamCard card) {
- return new Pair(card.getType(), card.getId());
- }
-
- private void notifyListenersCardAdded(StreamCard card) {
- Iterator<IStreamConsumer> iterator = mConsumers.iterator();
-
- while (iterator.hasNext()) {
- IStreamConsumer consumer = iterator.next();
- try {
- consumer.onStreamCardAdded(card);
- } catch (DeadObjectException e) {
- iterator.remove();
- Log.w(TAG, "Dead Stream Listener removed");
- } catch (RemoteException e) {
- Log.e(TAG, e.getMessage());
- }
- }
-
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Notify StreamCard added, card: " + card);
- Log.d(TAG, "Card Extension: " + card.getCardExtension());
- }
- }
-
- private void notifyListenersCardRemoved(StreamCard card) {
- Iterator<IStreamConsumer> iterator = mConsumers.iterator();
-
- while (iterator.hasNext()) {
- IStreamConsumer consumer = iterator.next();
- try {
- consumer.onStreamCardRemoved(card);
- } catch (DeadObjectException e) {
- iterator.remove();
- Log.w(TAG, "Dead Stream Listener removed");
- } catch (RemoteException e) {
- Log.e(TAG, e.getMessage());
- }
- }
-
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Notify StreamCard removed, card type: " + card.getType());
- }
- }
-
- private void rankStreamCard(StreamCard card) {
- // TODO: move this into a separate class once we introduce the actual ranking.
- card.setPriority(1);
- }
-}
diff --git a/src/com/android/car/stream/StreamServiceConstants.java b/src/com/android/car/stream/StreamServiceConstants.java
deleted file mode 100644
index 521a512..0000000
--- a/src/com/android/car/stream/StreamServiceConstants.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
- *
- * 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.android.car.stream;
-
-/**
- * A class that holds various common constants used through the Stream service.
- */
-public class StreamServiceConstants {
- /**
- * The id that should be used for all media cards. Using a common id for all media cards
- * ensure that only one will show at a time.
- */
- public static long MEDIA_CARD_ID = -1L;
-
- /**
- * The id within the {@link MediaPlaybackExtension} that indicates this MediaPlaybackExtension
- * is coming from a non-radio application.
- *
- * <p>The reason that this id is necessary is because the radio does not use the MediaSession
- * to notify of playback state. Thus, notifications about playback state for media apps and
- * radio are not guaranteed to be in order. This id along with {@link #MEDIA_EXTENSION_ID_RADIO}
- * will help differentiate which application is firing a change.
- */
- public static long MEDIA_EXTENSION_ID_NON_RADIO = -1L;
-
- /**
- * The id within the {@link MediaPlaybackExtension} that indicates this MediaPlaybackExtension
- * is coming from a radio application.
- *
- * @see {@link #MEDIA_EXTENSION_ID_NON_RADIO}
- */
- public static long MEDIA_EXTENSION_ID_RADIO= -2L;
-
-
- private StreamServiceConstants() {}
-}
diff --git a/src/com/android/car/stream/media/MediaAppInfo.java b/src/com/android/car/stream/media/MediaAppInfo.java
deleted file mode 100644
index 34980c3..0000000
--- a/src/com/android/car/stream/media/MediaAppInfo.java
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
- *
- * 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.android.car.stream.media;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ServiceInfo;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.util.Log;
-
-/**
- * An immutable class which hold the the information about the currently connected media app, if
- * it supports {@link android.service.media.MediaBrowserService}.
- */
-public class MediaAppInfo {
- private static final String TAG = "MediaAppInfo";
- private static final String KEY_SMALL_ICON =
- "com.google.android.gms.car.notification.SmallIcon";
-
- /** Third-party defined application theme to use **/
- private static final String THEME_META_DATA_NAME
- = "com.google.android.gms.car.application.theme";
-
- private final ComponentName mComponentName;
- private final Resources mPackageResources;
- private final String mAppName;
- private final String mPackageName;
- private final int mSmallIcon;
-
- private int mPrimaryColor;
- private int mPrimaryColorDark;
- private int mAccentColor;
-
- public MediaAppInfo(Context context, String packageName) {
- Resources resources = null;
- try {
- resources = context.getPackageManager().getResourcesForApplication(packageName);
- } catch (PackageManager.NameNotFoundException e) {
- Log.e(TAG, "Unable to get resources for " + packageName);
- }
- mPackageResources = resources;
-
- mComponentName = MediaUtils.getMediaBrowserService(packageName, context);
- String appName = null;
- int smallIconResId = 0;
- try {
- PackageManager packageManager = context.getPackageManager();
- ServiceInfo serviceInfo = null;
- ApplicationInfo appInfo = packageManager.getApplicationInfo(packageName,
- PackageManager.GET_META_DATA);
-
- int labelResId;
-
- if (mComponentName != null) {
- serviceInfo =
- packageManager.getServiceInfo(mComponentName, PackageManager.GET_META_DATA);
- smallIconResId = serviceInfo.metaData == null ? 0 : serviceInfo.metaData.getInt
- (KEY_SMALL_ICON, 0);
- labelResId = serviceInfo.labelRes;
- } else {
- Log.w(TAG, "Service label is null for " + packageName +
- ". Falling back to app name.");
- labelResId = appInfo.labelRes;
- }
-
- int appTheme = 0;
- if (serviceInfo != null && serviceInfo.metaData != null) {
- appTheme = serviceInfo.metaData.getInt(THEME_META_DATA_NAME);
- }
- if (appTheme == 0 && appInfo.metaData != null) {
- appTheme = appInfo.metaData.getInt(THEME_META_DATA_NAME);
- }
- if (appTheme == 0) {
- appTheme = appInfo.theme;
- }
-
- fetchAppColors(packageName, appTheme, context);
- appName = (labelResId == 0 || mPackageResources == null) ? null
- : mPackageResources.getString(labelResId);
- } catch (PackageManager.NameNotFoundException e) {
- Log.e(TAG, "Got a component that doesn't exist (" + packageName + ")");
- }
- mSmallIcon = smallIconResId;
- mAppName = appName;
-
- mPackageName = packageName;
- }
-
- public ComponentName getComponentName() {
- return mComponentName;
- }
-
- public String getAppName() {
- return mAppName;
- }
-
- public int getSmallIcon() {
- return mSmallIcon;
- }
-
- public String getPackageName() {
- return mPackageName;
- }
-
- public Resources getPackageResources() {
- return mPackageResources;
- }
-
- public int getMediaClientPrimaryColor() {
- return mPrimaryColor;
- }
-
- public int getMediaClientPrimaryColorDark() {
- return mPrimaryColorDark;
- }
-
- public int getMediaClientAccentColor() {
- return mAccentColor;
- }
-
- private void fetchAppColors(String packageName, int appTheme, Context context) {
- TypedArray ta = null;
- try {
- Context packageContext = context.createPackageContext(packageName, 0);
- packageContext.setTheme(appTheme);
- Resources.Theme theme = packageContext.getTheme();
- ta = theme.obtainStyledAttributes(new int[]{
- android.R.attr.colorPrimary,
- android.R.attr.colorAccent,
- android.R.attr.colorPrimaryDark
- });
- int defaultColor =
- context.getColor(android.R.color.holo_green_light);
- mPrimaryColor = ta.getColor(0, defaultColor);
- mAccentColor = ta.getColor(1, defaultColor);
- mPrimaryColorDark = ta.getColor(2, defaultColor);
- } catch (PackageManager.NameNotFoundException e) {
- Log.e(TAG, "Unable to update media client package attributes.", e);
- } finally {
- if (ta != null) {
- ta.recycle();
- }
- }
- }
-} \ No newline at end of file
diff --git a/src/com/android/car/stream/media/MediaConverter.java b/src/com/android/car/stream/media/MediaConverter.java
deleted file mode 100644
index 41b9f51..0000000
--- a/src/com/android/car/stream/media/MediaConverter.java
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
- *
- * 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.android.car.stream.media;
-
-import android.app.PendingIntent;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.drawable.VectorDrawable;
-import android.view.KeyEvent;
-import android.widget.RemoteViews;
-import com.android.car.stream.MediaPlaybackExtension;
-import com.android.car.stream.R;
-import com.android.car.stream.StreamCard;
-import com.android.car.stream.StreamConstants;
-import com.android.car.stream.StreamServiceConstants;
-
-/**
- * A converter that creates a {@link StreamCard} for currently playing media.
- */
-public class MediaConverter {
- private final PendingIntent mGotoMediaFacetAction;
- private final PendingIntent mPauseAction;
- private final PendingIntent mSkipToNextAction;
- private final PendingIntent mSkipToPreviousAction;
- private final PendingIntent mPlayAction;
- private final PendingIntent mStopAction;
-
- private Bitmap mPlayIcon;
- private Bitmap mPauseIcon;
-
- public MediaConverter(Context context) {
- String mediaPackage = context.getString(R.string.car_media_component_package);
- mGotoMediaFacetAction = createGoToMediaFacetIntent(context, mediaPackage);
-
- mPauseAction = getMediaActionIntent(context,
- new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_PAUSE),
- KeyEvent.KEYCODE_MEDIA_PAUSE /* requestCode */);
-
- mSkipToNextAction = getMediaActionIntent(context,
- new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_SKIP_FORWARD),
- KeyEvent.KEYCODE_MEDIA_SKIP_FORWARD /* requestCode */);
-
- mSkipToPreviousAction = getMediaActionIntent(context,
- new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_SKIP_BACKWARD),
- KeyEvent.KEYCODE_MEDIA_SKIP_BACKWARD /* requestCode */);
-
-
- mPlayAction = getMediaActionIntent(context,
- new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_PLAY),
- KeyEvent.KEYCODE_MEDIA_PLAY /* requestCode */);
-
- mStopAction = getMediaActionIntent(context,
- new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_STOP),
- KeyEvent.KEYCODE_MEDIA_STOP /* requestCode */);
-
- int iconSize = context.getResources()
- .getDimensionPixelSize(R.dimen.stream_card_secondary_icon_dimen);
- mPlayIcon = getBitmap((VectorDrawable)
- context.getDrawable(R.drawable.ic_play_arrow), iconSize, iconSize);
- mPauseIcon = getBitmap((VectorDrawable)
- context.getDrawable(R.drawable.ic_pause), iconSize, iconSize);
- }
-
- public StreamCard convert(
- String title,
- String subtitle,
- Bitmap albumArt,
- int appAccentColor,
- String appName,
- boolean canSkipToNext,
- boolean canSkipToPrevious,
- boolean hasPause,
- boolean isPlaying) {
-
- StreamCard.Builder builder = new StreamCard.Builder(StreamConstants.CARD_TYPE_MEDIA,
- StreamConstants.MEDIA_CARD_ID, System.currentTimeMillis());
- builder.setClickAction(mGotoMediaFacetAction);
- builder.setPrimaryText(title);
- builder.setSecondaryText(subtitle);
- Bitmap icon = isPlaying ? mPlayIcon : mPauseIcon;
- builder.setPrimaryIcon(icon);
-
- MediaPlaybackExtension extension = new MediaPlaybackExtension(title, subtitle, albumArt,
- appAccentColor, canSkipToNext, canSkipToPrevious, hasPause, isPlaying, appName,
- mStopAction, mPauseAction, mPlayAction, mSkipToNextAction, mSkipToPreviousAction);
-
- builder.setCardExtension(extension);
- return builder.build();
- }
-
- /**
- * Attaches a {@link PendingIntent} to the given {@link RemoteViews}. The PendingIntent will
- * send the user to the CarMediaApp when touched. Note that this does not resolve to the
- * application currently in the media card; instead, it just opens the last music app. For
- * example, if the card is generated by Google Play Music, but the last opened music app
- * was Spotify, then Spotify will open when the music card is tapped.
- */
- private PendingIntent createGoToMediaFacetIntent(Context context, String mediaPackage) {
- Intent intent = context.getPackageManager().getLaunchIntentForPackage(mediaPackage);
- intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT);
-
- return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
- }
-
- private PendingIntent getMediaActionIntent(Context context, KeyEvent ke, int requestCode) {
- Intent i = new Intent(Intent.ACTION_MEDIA_BUTTON);
- i.setPackage(context.getPackageName());
- i.putExtra(Intent.EXTRA_KEY_EVENT, ke);
-
- PendingIntent pendingIntent =
- PendingIntent.getBroadcast(
- context,
- requestCode,
- i,
- PendingIntent.FLAG_CANCEL_CURRENT
- );
- return pendingIntent;
- }
-
- private static Bitmap getBitmap(VectorDrawable vectorDrawable, int width, int height) {
- Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(bitmap);
- vectorDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
- vectorDrawable.draw(canvas);
- return bitmap;
- }
-}
diff --git a/src/com/android/car/stream/media/MediaPlaybackMonitor.java b/src/com/android/car/stream/media/MediaPlaybackMonitor.java
deleted file mode 100644
index c360770..0000000
--- a/src/com/android/car/stream/media/MediaPlaybackMonitor.java
+++ /dev/null
@@ -1,342 +0,0 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
- *
- * 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.android.car.stream.media;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.media.MediaDescription;
-import android.media.MediaMetadata;
-import android.media.session.PlaybackState;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.Message;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.text.TextUtils;
-import android.util.Log;
-import com.android.car.apps.common.BitmapDownloader;
-import com.android.car.apps.common.BitmapWorkerOptions;
-import com.android.car.stream.R;
-
-/**
- * An service which connects to {@link MediaStateManager} for media updates (playback state and
- * metadata) and notifies listeners for these changes.
- * <p/>
- */
-public class MediaPlaybackMonitor implements MediaStateManager.Listener {
- protected static final String TAG = "MediaPlaybackMonitor";
-
- // MSG for metadata update handler
- private static final int MSG_UPDATE_METADATA = 1;
- private static final int MSG_IMAGE_DOWNLOADED = 2;
- private static final int MSG_NEW_ALBUM_ART_RECEIVED = 3;
-
- public interface MediaPlaybackMonitorListener {
- void onPlaybackStateChanged(PlaybackState state);
-
- void onMetadataChanged(String title, String text, Bitmap art, int color, String appName);
-
- void onAlbumArtUpdated(Bitmap albumArt);
-
- void onNewAppConnected();
-
- void removeMediaStreamCard();
- }
-
- private static final String[] PREFERRED_BITMAP_ORDER = {
- MediaMetadata.METADATA_KEY_ALBUM_ART,
- MediaMetadata.METADATA_KEY_ART,
- MediaMetadata.METADATA_KEY_DISPLAY_ICON
- };
-
- private static final String[] PREFERRED_URI_ORDER = {
- MediaMetadata.METADATA_KEY_ALBUM_ART_URI,
- MediaMetadata.METADATA_KEY_ART_URI,
- MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI
- };
-
- private MediaMetadata mCurrentMetadata;
- private MediaStatusUpdateHandler mMediaStatusUpdateHandler;
- private MediaAppInfo mCurrentMediaAppInfo;
- private MediaPlaybackMonitorListener mMonitorListener;
-
- private Context mContext;
-
- private final int mIconSize;
-
- public MediaPlaybackMonitor(Context context, @NonNull MediaPlaybackMonitorListener callback) {
- mContext = context;
- mMonitorListener = callback;
- mIconSize = mContext.getResources().getDimensionPixelSize(R.dimen.stream_media_icon_size);
- }
-
- public final void start() {
- mMediaStatusUpdateHandler = new MediaStatusUpdateHandler();
- }
-
- public final void stop() {
- if (mMediaStatusUpdateHandler != null) {
- mMediaStatusUpdateHandler.removeCallbacksAndMessages(null);
- mMediaStatusUpdateHandler = null;
- }
- }
-
- @Override
- public void onMediaSessionConnected(PlaybackState state, MediaMetadata metaData,
- MediaAppInfo appInfo) {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "MediaSession onConnected called");
- }
-
- // If the current media app is not the same as the new media app, reset
- // the media app in MediaStreamManager
- if (mCurrentMediaAppInfo == null
- || !mCurrentMediaAppInfo.getPackageName().equals(appInfo.getPackageName())) {
- mMonitorListener.onNewAppConnected();
- if (mMediaStatusUpdateHandler != null) {
- mMediaStatusUpdateHandler.removeCallbacksAndMessages(null);
- }
- mCurrentMediaAppInfo = appInfo;
- }
-
- if (metaData != null) {
- onMetadataChanged(metaData);
- }
-
- if (state != null) {
- onPlaybackStateChanged(state);
- }
- }
-
- @Override
- public void onPlaybackStateChanged(@Nullable PlaybackState state) {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "onPlaybackStateChanged called " + state.getState());
- }
-
- if (state == null) {
- Log.w(TAG, "playback state is null in onPlaybackStateChanged");
- mMonitorListener.removeMediaStreamCard();
- return;
- }
-
- if (mMonitorListener != null) {
- mMonitorListener.onPlaybackStateChanged(state);
- }
- }
-
- @Override
- public void onMetadataChanged(@Nullable MediaMetadata metadata) {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "onMetadataChanged called");
- }
- if (metadata == null) {
- mMonitorListener.removeMediaStreamCard();
- return;
- }
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "received " + metadata.getDescription());
- }
- // Compare the new metadata and the last we have posted notification for. If both
- // metadata and album art are the same, just ignore and return. If the album art is new,
- // update the stream item with the new album art.
- MediaDescription currentDescription = mCurrentMetadata == null ?
- null : mCurrentMetadata.getDescription();
-
- if (!MediaUtils.isSameMediaDescription(metadata.getDescription(), currentDescription)) {
- Message msg =
- mMediaStatusUpdateHandler.obtainMessage(MSG_UPDATE_METADATA, metadata);
- // Remove obsolete notifications in the queue.
- mMediaStatusUpdateHandler.removeMessages(MSG_UPDATE_METADATA);
- mMediaStatusUpdateHandler.sendMessage(msg);
- } else {
- Bitmap newBitmap = metadata.getDescription().getIconBitmap();
- if (newBitmap == null) {
- return;
- }
- if (newBitmap.sameAs(mMediaStatusUpdateHandler.getCurrentIcon())) {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Received duplicate metadata, ignoring...");
- }
- } else {
- // same metadata, but new album art
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Received metadata with new album art");
- }
- Message msg = mMediaStatusUpdateHandler
- .obtainMessage(MSG_NEW_ALBUM_ART_RECEIVED, newBitmap);
- mMediaStatusUpdateHandler.removeMessages(MSG_NEW_ALBUM_ART_RECEIVED);
- mMediaStatusUpdateHandler.sendMessage(msg);
- }
- }
- }
-
- @Override
- public void onSessionDestroyed() {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Media session destroyed");
- }
- mMonitorListener.removeMediaStreamCard();
- }
-
- private class BitmapCallback extends BitmapDownloader.BitmapCallback {
- final private int mSeq;
-
- public BitmapCallback(int seq) {
- mSeq = seq;
- }
-
- @Override
- public void onBitmapRetrieved(Bitmap bitmap) {
- if (mMediaStatusUpdateHandler == null) {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "The callback comes after we finish");
- }
- return;
- }
- Message msg = mMediaStatusUpdateHandler.obtainMessage(MSG_IMAGE_DOWNLOADED,
- mSeq, 0, bitmap);
- mMediaStatusUpdateHandler.sendMessage(msg);
- }
- }
-
- private class MediaStatusUpdateHandler extends Handler {
- private int mSeq = 0;
- private BitmapCallback mCallback;
- private MediaMetadata mMetadata;
- private String mTitle;
- private String mSubtitle;
- private Bitmap mIcon;
- private Uri mIconUri;
- private final BitmapDownloader mDownloader = BitmapDownloader.getInstance(mContext);
-
- private void extractMetadata(MediaMetadata metadata) {
- if (metadata == mMetadata) {
- // We are up to date and must return here, because we've already recycled the bitmap
- // inside it.
- return;
- }
- // keep a reference so we know which metadata we have stored.
- mMetadata = metadata;
- MediaDescription description = metadata.getDescription();
- mTitle = description.getTitle() == null ? null : description.getTitle().toString();
- mSubtitle = description.getSubtitle() == null ?
- null : description.getSubtitle().toString();
- final Bitmap originalBitmap = getMetadataBitmap(metadata);
- if (originalBitmap != null) {
- mIcon = originalBitmap;
- } else {
- mIcon = null;
- }
- mIconUri = getMetadataIconUri(metadata);
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Album Art Uri: " + mIconUri);
- }
- }
-
- private Uri getMetadataIconUri(MediaMetadata metadata) {
- // Get the best Uri we can find
- for (int i = 0; i < PREFERRED_URI_ORDER.length; i++) {
- String iconUri = metadata.getString(PREFERRED_URI_ORDER[i]);
- if (!TextUtils.isEmpty(iconUri)) {
- return Uri.parse(iconUri);
- }
- }
- return null;
- }
-
- private Bitmap getMetadataBitmap(MediaMetadata metadata) {
- // Get the best art bitmap we can find
- for (int i = 0; i < PREFERRED_BITMAP_ORDER.length; i++) {
- Bitmap bitmap = metadata.getBitmap(PREFERRED_BITMAP_ORDER[i]);
- if (bitmap != null) {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Retrieved bitmap type: " + PREFERRED_BITMAP_ORDER[i]
- + " w: " + bitmap.getWidth()
- + " h: " + bitmap.getHeight());
- }
- return bitmap;
- }
- }
- return null;
- }
-
- public Bitmap getCurrentIcon() {
- return mIcon;
- }
-
- @Override
- public void handleMessage(Message msg) {
- MediaAppInfo mediaAppInfo = mCurrentMediaAppInfo;
- int color = mediaAppInfo.getMediaClientAccentColor();
- String appName = mediaAppInfo.getAppName();
- switch (msg.what) {
- case MSG_UPDATE_METADATA:
- mSeq++;
- MediaMetadata metadata = (MediaMetadata) msg.obj;
- if (metadata == null) {
- Log.w(TAG, "media metadata is null!");
- return;
- }
- extractMetadata(metadata);
- if (mCallback != null) {
- // it's ok to cancel a callback that has already been called, the downloader
- // will just ignore the operation.
- mDownloader.cancelDownload(mCallback);
- mCallback = null;
- }
- if (mIcon != null) {
- mMonitorListener.onMetadataChanged(mTitle, mSubtitle, mIcon,
- color, appName);
- } else if (mIconUri != null) {
- mCallback = new BitmapCallback(mSeq);
- mDownloader.getBitmap(
- new BitmapWorkerOptions.Builder(mContext)
- .resource(mIconUri).width(mIconSize)
- .height(mIconSize).build(), mCallback);
- } else {
- mMonitorListener.onMetadataChanged(mTitle, mSubtitle, mIcon,
- color, appName);
- }
- // Only set mCurrentMetadata after we have updated the listener (if the
- // bitmap is downloaded asynchronously, that is fine too. The stream card will
- // be posted, when image is downloaded.)
- mCurrentMetadata = metadata;
- break;
-
- case MSG_IMAGE_DOWNLOADED:
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Image downloaded...");
- }
- int seq = msg.arg1;
- Bitmap bitmap = (Bitmap) msg.obj;
- if (seq == mSeq) {
- mMonitorListener.onMetadataChanged(mTitle, mSubtitle, bitmap, color, appName);
- }
- break;
-
- case MSG_NEW_ALBUM_ART_RECEIVED:
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Received a new album art...");
- }
- Bitmap newAlbumArt = (Bitmap) msg.obj;
- mMonitorListener.onAlbumArtUpdated(newAlbumArt);
- break;
- default:
- }
- }
- }
-}
diff --git a/src/com/android/car/stream/media/MediaStateManager.java b/src/com/android/car/stream/media/MediaStateManager.java
deleted file mode 100644
index e574852..0000000
--- a/src/com/android/car/stream/media/MediaStateManager.java
+++ /dev/null
@@ -1,248 +0,0 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
- *
- * 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.android.car.stream.media;
-
-import android.content.Context;
-import android.media.MediaMetadata;
-import android.media.session.MediaController;
-import android.media.session.MediaSessionManager;
-import android.media.session.PlaybackState;
-import android.os.Handler;
-import android.os.Looper;
-import android.support.annotation.MainThread;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.util.Log;
-import android.view.KeyEvent;
-import com.android.car.apps.common.util.Assert;
-
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Set;
-
-/**
- * A class to listen for changes in sessions from {@link MediaSessionManager}. It also notifies
- * listeners of changes in the playback state or metadata.
- */
-public class MediaStateManager {
- private static final String TAG = "MediaStateManager";
- private static final String TELECOM_PACKAGE = "com.android.server.telecom";
-
- private final Context mContext;
-
- private MediaAppInfo mConnectedAppInfo;
- private MediaController mController;
- private Handler mHandler;
- private final Set<Listener> mListeners;
-
- public interface Listener {
- void onMediaSessionConnected(PlaybackState playbackState, MediaMetadata metaData,
- MediaAppInfo appInfo);
-
- void onPlaybackStateChanged(@Nullable PlaybackState state);
-
- void onMetadataChanged(@Nullable MediaMetadata metadata);
-
- void onSessionDestroyed();
- }
-
- public MediaStateManager(@NonNull Context context) {
- mContext = context;
- mHandler = new Handler(Looper.getMainLooper());
- mListeners = new LinkedHashSet<>();
- }
-
- public void start() {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Starting MediaStateManager");
- }
- MediaSessionManager sessionManager
- = (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
-
- try {
- sessionManager.addOnActiveSessionsChangedListener(mSessionChangedListener, null);
-
- List<MediaController> controllers = sessionManager.getActiveSessions(null);
- updateMediaController(controllers);
- } catch (SecurityException e) {
- // User hasn't granted the permission so we should just go away silently.
- }
- }
-
- @MainThread
- public void destroy() {
- Assert.isMainThread();
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "destroy()");
- }
- stop();
- mListeners.clear();
- mHandler = null;
- }
-
- @MainThread
- public void stop() {
- Assert.isMainThread();
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "stop()");
- }
-
- if (mController != null) {
- mController.unregisterCallback(mMediaControllerCallback);
- mController = null;
- }
- // Calling this with null will clear queue of callbacks and message. This needs to be done
- // here because prior to the above lines to disconnect and unregister the
- // controller a posted runnable to do work maybe have happened and thus we need to clear it
- // out to prevent race conditions.
- mHandler.removeCallbacksAndMessages(null);
- }
-
- public void dispatchMediaButton(KeyEvent keyEvent) {
- if (mController != null) {
- MediaController.TransportControls transportControls
- = mController.getTransportControls();
- int eventId = keyEvent.getKeyCode();
-
- switch (eventId) {
- case KeyEvent.KEYCODE_MEDIA_SKIP_BACKWARD:
- transportControls.skipToPrevious();
- break;
- case KeyEvent.KEYCODE_MEDIA_SKIP_FORWARD:
- transportControls.skipToNext();
- break;
- case KeyEvent.KEYCODE_MEDIA_PLAY:
- transportControls.play();
- break;
- case KeyEvent.KEYCODE_MEDIA_PAUSE:
- transportControls.pause();
- break;
- case KeyEvent.KEYCODE_MEDIA_STOP:
- transportControls.stop();
- break;
- default:
- mController.dispatchMediaButtonEvent(keyEvent);
- }
- }
- }
-
- public void addListener(@NonNull Listener listener) {
- mListeners.add(listener);
- }
-
- public void removeListener(@NonNull Listener listener) {
- mListeners.remove(listener);
- }
-
- private void updateMediaController(List<MediaController> controllers) {
- if (controllers.size() > 0) {
- // If the telecom package is trying to onStart a media session, ignore it
- // so that the existing media item continues to appear in the stream.
- if (TELECOM_PACKAGE.equals(controllers.get(0).getPackageName())) {
- return;
- }
-
- if (mController != null) {
- mController.unregisterCallback(mMediaControllerCallback);
- }
- // Currently the first controller is the active one playing music.
- // If this is no longer the case, consider checking notification listener
- // for a MediaStyle notification to get currently playing media app.
- mController = controllers.get(0);
- mController.registerCallback(mMediaControllerCallback);
-
- mConnectedAppInfo = new MediaAppInfo(mContext, mController.getPackageName());
-
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "updating media controller");
- }
-
- for (Listener listener : mListeners) {
- listener.onMediaSessionConnected(mController.getPlaybackState(),
- mController.getMetadata(), mConnectedAppInfo);
- }
- } else {
- Log.w(TAG, "Updating controllers with an empty list!");
- }
- }
-
- public static boolean isMainThread() {
- return Looper.myLooper() == Looper.getMainLooper();
- }
-
- private final MediaSessionManager.OnActiveSessionsChangedListener
- mSessionChangedListener = new MediaSessionManager.OnActiveSessionsChangedListener() {
- @Override
- public void onActiveSessionsChanged(List<MediaController> controllers) {
- updateMediaController(controllers);
- }
- };
-
- private final MediaController.Callback mMediaControllerCallback =
- new MediaController.Callback() {
- @Override
- public void onPlaybackStateChanged(@NonNull final PlaybackState state) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "onPlaybackStateChanged(" + state + ")");
- }
- for (Listener listener : mListeners) {
- listener.onPlaybackStateChanged(state);
- }
- }
- });
- }
-
- @Override
- public void onMetadataChanged(@Nullable final MediaMetadata metadata) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "onMetadataChanged(" + metadata + ")");
- }
- for (Listener listener : mListeners) {
- listener.onMetadataChanged(metadata);
- }
- }
- });
- }
-
- @Override
- public void onSessionDestroyed() {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "onSessionDestroyed()");
- }
-
- mConnectedAppInfo = null;
- if (mController != null) {
- mController.unregisterCallback(mMediaControllerCallback);
- mController = null;
- }
-
- for (Listener listener : mListeners) {
- listener.onSessionDestroyed();
- }
- }
- });
- }
- };
-}
diff --git a/src/com/android/car/stream/media/MediaStreamProducer.java b/src/com/android/car/stream/media/MediaStreamProducer.java
deleted file mode 100644
index 8808f14..0000000
--- a/src/com/android/car/stream/media/MediaStreamProducer.java
+++ /dev/null
@@ -1,238 +0,0 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
- *
- * 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.android.car.stream.media;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.graphics.Bitmap;
-import android.media.session.PlaybackState;
-import android.util.Log;
-import android.view.KeyEvent;
-import com.android.car.stream.StreamCard;
-import com.android.car.stream.StreamProducer;
-
-/**
- * Produces {@link StreamCard} on media playback or metadata changes.
- */
-public class MediaStreamProducer extends StreamProducer
- implements MediaPlaybackMonitor.MediaPlaybackMonitorListener {
- private static final String TAG = "MediaStreamProducer";
-
- private MediaPlaybackMonitor mPlaybackMonitor;
- private MediaStateManager mMediaStateManager;
- private MediaKeyReceiver mMediaKeyReceiver;
- private MediaConverter mConverter;
-
- private StreamCard mCurrentMediaStreamCard;
-
- private boolean mHasReceivedPlaybackState;
- private boolean mHasReceivedMetadata;
-
- // Current playback state of the media session.
- private boolean mIsPlaying;
- private boolean mHasPause;
- private boolean mCanSkipToNext;
- private boolean mCanSkipToPrevious;
-
- private String mTitle;
- private String mSubtitle;
- private Bitmap mAlbumArt;
- private int mAppAccentColor;
- private String mAppName;
-
- public MediaStreamProducer(Context context) {
- super(context);
- mConverter = new MediaConverter(context);
- }
-
- @Override
- public void start() {
- super.start();
- mPlaybackMonitor = new MediaPlaybackMonitor(mContext,
- MediaStreamProducer.this /* MediaPlaybackMonitorListener */);
- mPlaybackMonitor.start();
-
- mMediaKeyReceiver = new MediaKeyReceiver();
- mContext.registerReceiver(mMediaKeyReceiver,
- new IntentFilter(Intent.ACTION_MEDIA_BUTTON));
-
- mMediaStateManager = new MediaStateManager(mContext);
- mMediaStateManager.addListener(mPlaybackMonitor);
- mMediaStateManager.start();
- }
-
- @Override
- public void stop() {
- mPlaybackMonitor.stop();
- mMediaStateManager.destroy();
-
- mPlaybackMonitor = null;
- mMediaStateManager = null;
-
- mContext.unregisterReceiver(mMediaKeyReceiver);
- mMediaKeyReceiver = null;
- super.stop();
- }
-
- private class MediaKeyReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- String intentAction = intent.getAction();
- if (Intent.ACTION_MEDIA_BUTTON.equals(intentAction)) {
- KeyEvent event = intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
- if (event == null) {
- return;
- }
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Received media key " + event.getKeyCode());
- }
- mMediaStateManager.dispatchMediaButton(event);
- }
- }
- }
-
- public void onPlaybackStateChanged(PlaybackState state) {
- //Some media apps tend to spam playback state changes. Check if the playback state changes
- // are relevant. If it is the same, don't bother updating and posting to the stream.
- if (isDuplicatePlaybackState(state)) {
- return;
- }
-
- int playbackState = state.getState();
- mHasPause = ((state.getActions() & PlaybackState.ACTION_PAUSE) != 0);
- if (!mHasPause) {
- mHasPause = ((state.getActions() & PlaybackState.ACTION_PLAY_PAUSE) != 0);
- }
- mCanSkipToNext = ((state.getActions() & PlaybackState.ACTION_SKIP_TO_NEXT) != 0);
- mCanSkipToPrevious = ((state.getActions() & PlaybackState.ACTION_SKIP_TO_PREVIOUS) != 0);
- if (playbackState == PlaybackState.STATE_PLAYING
- || playbackState == PlaybackState.STATE_BUFFERING) {
- mIsPlaying = true;
- } else {
- mIsPlaying = false;
- }
- mHasReceivedPlaybackState = true;
- maybeUpdateStreamCard();
- }
-
- private void maybeUpdateStreamCard() {
- if (mHasReceivedPlaybackState && mHasReceivedMetadata) {
- mCurrentMediaStreamCard = mConverter.convert(mTitle, mSubtitle, mAlbumArt,
- mAppAccentColor, mAppName, mCanSkipToNext, mCanSkipToPrevious,
- mHasPause, mIsPlaying);
- if (mCurrentMediaStreamCard == null) {
- Log.w(TAG, "Media Card was not created");
- return;
- }
-
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Media Card posted");
- }
- postCard(mCurrentMediaStreamCard);
- }
- }
-
- public void onMetadataChanged(String title, String subtitle, Bitmap albumArt, int color,
- String appName) {
- //Some media apps tend to spam metadata state changes. Check if the playback state changes
- // are relevant. If it is the same, don't bother updating and posting to the stream.
- if (isSameString(title, mTitle)
- && isSameString(subtitle, mSubtitle)
- && isSameBitmap(albumArt, albumArt)
- && color == mAppAccentColor
- && isSameString(appName, mAppName)) {
- return;
- }
-
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Update notification.");
- }
-
- mTitle = title;
- mSubtitle = subtitle;
- mAlbumArt = albumArt;
- mAppAccentColor = color;
- mAppName = appName;
-
- mHasReceivedMetadata = true;
- maybeUpdateStreamCard();
- }
-
- private boolean isDuplicatePlaybackState(PlaybackState state) {
- if (!mHasReceivedPlaybackState) {
- return false;
- }
- int playbackState = state.getState();
-
- boolean hasPause
- = ((state.getActions() & PlaybackState.ACTION_PAUSE) != 0);
- boolean canSkipToNext
- = ((state.getActions() & PlaybackState.ACTION_SKIP_TO_NEXT) != 0);
- boolean canSkipToPrevious
- = ((state.getActions() & PlaybackState.ACTION_SKIP_TO_PREVIOUS) != 0);
-
- boolean isPlaying = playbackState == PlaybackState.STATE_PLAYING
- || playbackState == PlaybackState.STATE_BUFFERING;
-
- return (hasPause == mHasPause
- && canSkipToNext == mCanSkipToNext
- && canSkipToPrevious == mCanSkipToPrevious
- && isPlaying == mIsPlaying);
- }
-
- @Override
- public void onAlbumArtUpdated(Bitmap albumArt) {
- mAlbumArt = albumArt;
- maybeUpdateStreamCard();
- }
-
- @Override
- public void onNewAppConnected() {
- mHasReceivedMetadata = false;
- mHasReceivedPlaybackState = false;
- removeCard(mCurrentMediaStreamCard);
- mCurrentMediaStreamCard = null;
-
- // clear out all existing values
- mTitle = null;
- mSubtitle = null;
- mAlbumArt = null;
- mAppName = null;
- mAppAccentColor = 0;
- mCanSkipToNext = false;
- mCanSkipToPrevious = false;
- mHasPause = false;
- mIsPlaying = false;
- mIsPlaying = false;
- }
-
- @Override
- public void removeMediaStreamCard() {
- removeCard(mCurrentMediaStreamCard);
- mCurrentMediaStreamCard = null;
- }
-
- private boolean isSameBitmap(Bitmap bmp1, Bitmap bmp2) {
- return bmp1 == null
- ? bmp2 == null : (bmp1 == bmp2 && bmp1.getGenerationId() == bmp2.getGenerationId());
- }
-
- private boolean isSameString(CharSequence str1, CharSequence str2) {
- return str1 == null ? str2 == null : str1.equals(str2);
- }
-}
diff --git a/src/com/android/car/stream/media/MediaUtils.java b/src/com/android/car/stream/media/MediaUtils.java
deleted file mode 100644
index b271b2b..0000000
--- a/src/com/android/car/stream/media/MediaUtils.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
- *
- * 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.android.car.stream.media;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.media.MediaDescription;
-import android.service.media.MediaBrowserService;
-
-import java.util.List;
-import java.util.Objects;
-
-/**
- * Media related utility functions
- */
-public final class MediaUtils {
- /**
- * @return True if the two media descriptions are the same.
- */
- public static boolean isSameMediaDescription(MediaDescription description1,
- MediaDescription description2) {
- if ((description1 == null) && (description2 == null)) {
- return true;
- }
-
- if (description1 != null && description2 != null) {
- return Objects.equals(description1.getTitle(), description2.getTitle())
- && Objects.equals(description1.getSubtitle(), description2.getSubtitle());
- }
- return false;
- }
-
- /**
- * @return The component name of the {@link MediaBrowserService} for the given package name.
- */
- public static ComponentName getMediaBrowserService(String packageName,
- Context context) {
- Intent intent = new Intent(MediaBrowserService.SERVICE_INTERFACE);
- List<ResolveInfo> mediaApps = context.getPackageManager()
- .queryIntentServices(intent, PackageManager.GET_RESOLVED_FILTER);
-
- for (int i = 0; i < mediaApps.size(); i++) {
- ResolveInfo info = mediaApps.get(i);
- if (packageName.equals(info.serviceInfo.packageName)) {
- return new ComponentName(packageName, info.serviceInfo.name /* className */);
- }
- }
- return null;
- }
-}
diff --git a/src/com/android/car/stream/notifications/StreamNotificationListenerService.java b/src/com/android/car/stream/notifications/StreamNotificationListenerService.java
deleted file mode 100644
index 6e0c39b..0000000
--- a/src/com/android/car/stream/notifications/StreamNotificationListenerService.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
- *
- * 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.android.car.stream.notifications;
-
-import android.service.notification.NotificationListenerService;
-import android.service.notification.StatusBarNotification;
-import android.util.Log;
-
-/**
- * A listener to intercept notifications for the stream.
- */
-public class StreamNotificationListenerService extends NotificationListenerService {
- private static final String TAG = "NotificationListener";
-
- @Override
- public void onNotificationPosted(StatusBarNotification sbn) {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Notification received");
- }
- }
-
- @Override
- public void onNotificationRemoved(StatusBarNotification sbn) {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Notification removed");
- }
- }
-}
diff --git a/src/com/android/car/stream/radio/RadioConverter.java b/src/com/android/car/stream/radio/RadioConverter.java
deleted file mode 100644
index f3ef426..0000000
--- a/src/com/android/car/stream/radio/RadioConverter.java
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
- *
- * 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.android.car.stream.radio;
-
-import android.app.PendingIntent;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.drawable.VectorDrawable;
-import android.support.annotation.ColorInt;
-import com.android.car.radio.service.RadioStation;
-import com.android.car.stream.MediaPlaybackExtension;
-import com.android.car.stream.R;
-import com.android.car.stream.StreamCard;
-import com.android.car.stream.StreamConstants;
-import com.android.car.stream.StreamServiceConstants;
-
-/**
- * A converter that is responsible for transforming a {@link RadioStation} into a
- * {@link StreamCard}.
- */
-public class RadioConverter {
- /**
- * The separator between the radio channel and band (e.g. between 99.7 and FM).
- */
- private static final String CHANNEL_AND_BAND_SEPARATOR = " ";
-
- private final Context mContext;
-
- private final PendingIntent mGoToRadioAction;
- private final PendingIntent mPauseAction;
- private final PendingIntent mForwardSeekAction;
- private final PendingIntent mBackwardSeekAction;
- private final PendingIntent mPlayAction;
- private final PendingIntent mStopAction;
-
- @ColorInt
- private final int mAccentColor;
-
- private final Bitmap mPlayIcon;
- private final Bitmap mPauseIcon;
-
- public RadioConverter(Context context) {
- mContext = context;
-
- mGoToRadioAction = createGoToRadioIntent();
- mPauseAction = createRadioActionIntent(RadioStreamProducer.ACTION_PAUSE);
- mPlayAction = createRadioActionIntent(RadioStreamProducer.ACTION_PLAY);
- mStopAction = createRadioActionIntent(RadioStreamProducer.ACTION_STOP);
- mForwardSeekAction = createRadioActionIntent(RadioStreamProducer.ACTION_SEEK_FORWARD);
- mBackwardSeekAction = createRadioActionIntent(RadioStreamProducer.ACTION_SEEK_BACKWARD);
-
- mAccentColor = mContext.getColor(R.color.car_radio_accent_color);
-
- int iconSize = context.getResources()
- .getDimensionPixelSize(R.dimen.stream_card_secondary_icon_dimen);
- mPlayIcon = getBitmap((VectorDrawable)
- mContext.getDrawable(R.drawable.ic_play_arrow), iconSize, iconSize);
- mPauseIcon = getBitmap((VectorDrawable)
- mContext.getDrawable(R.drawable.ic_pause), iconSize, iconSize);
- }
-
- /**
- * Converts the given {@link RadioStation} and play status into a {@link StreamCard} that can
- * be used to display a radio card.
- */
- public StreamCard convert(RadioStation station, boolean isPlaying) {
- StreamCard.Builder builder = new StreamCard.Builder(StreamConstants.CARD_TYPE_MEDIA,
- StreamConstants.RADIO_CARD_ID, System.currentTimeMillis());
-
- builder.setClickAction(mGoToRadioAction);
-
- String title = createTitleText(station);
- builder.setPrimaryText(title);
-
- String subtitle = null;
- if (station.getRds() != null) {
- subtitle = station.getRds().getProgramService();
- builder.setSecondaryText(subtitle);
- }
-
- Bitmap icon = isPlaying ? mPlayIcon : mPauseIcon;
- builder.setPrimaryIcon(icon);
-
- MediaPlaybackExtension extension = new MediaPlaybackExtension(title, subtitle,
- null /* albumArt */, mAccentColor, true /* canSkipToNext */,
- true /* canSkipToPrevious */, true /* hasPause */, isPlaying,
- mContext.getString(R.string.radio_app_name), mStopAction, mPauseAction, mPlayAction,
- mForwardSeekAction, mBackwardSeekAction);
-
- builder.setCardExtension(extension);
- return builder.build();
- }
-
- /**
- * Returns the String that represents the title text of the radio card. The title should be
- * a combination of the current channel number and radio band.
- */
- private String createTitleText(RadioStation station) {
- int radioBand = station.getRadioBand();
- String channel = RadioFormatter.formatRadioChannel(radioBand,
- station.getChannelNumber());
- String band = RadioFormatter.formatRadioBand(mContext, radioBand);
-
- return channel + CHANNEL_AND_BAND_SEPARATOR + band;
- }
-
- /**
- * Returns an {@link Intent} that will take the user to the radio application.
- */
- private PendingIntent createGoToRadioIntent() {
- ComponentName radioComponent = new ComponentName(
- mContext.getString(R.string.car_radio_component_package),
- mContext.getString(R.string.car_radio_component_activity));
-
- Intent intent = new Intent();
- intent.setComponent(radioComponent);
- intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT);
-
- return PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
- }
-
- /**
- * Returns an {@link Intent} that will perform the given action.
- *
- * @param action One of the action values in {@link RadioStreamProducer}. e.g.
- * {@link RadioStreamProducer#ACTION_PAUSE}.
- */
- private PendingIntent createRadioActionIntent(int action) {
- Intent intent = new Intent(RadioStreamProducer.RADIO_INTENT_ACTION);
- intent.setPackage(mContext.getPackageName());
- intent.putExtra(RadioStreamProducer.RADIO_ACTION_EXTRA, action);
-
- return PendingIntent.getBroadcast(mContext, action /* requestCode */,
- intent, PendingIntent.FLAG_CANCEL_CURRENT);
- }
-
- /**
- * Returns a {@link Bitmap} that corresponds to the given {@link VectorDrawable}.
- */
- private static Bitmap getBitmap(VectorDrawable vectorDrawable, int width, int height) {
- Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(bitmap);
- vectorDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
- vectorDrawable.draw(canvas);
- return bitmap;
- }
-}
diff --git a/src/com/android/car/stream/radio/RadioFormatter.java b/src/com/android/car/stream/radio/RadioFormatter.java
deleted file mode 100644
index ad427d2..0000000
--- a/src/com/android/car/stream/radio/RadioFormatter.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
- *
- * 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.android.car.stream.radio;
-
-import android.content.Context;
-import android.hardware.radio.RadioManager;
-import com.android.car.radio.service.RadioStation;
-import com.android.car.stream.R;
-
-import java.text.DecimalFormat;
-import java.util.Locale;
-/**
- * Common formatters for displaying channel numbers for various radio channels and bands.
- */
-public final class RadioFormatter {
- private static final String FM_CHANNEL_FORMAT = "###.#";
- private static final String AM_CHANNEL_FORMAT = "####";
-
- private RadioFormatter() {}
-
- /**
- * The formatter for AM radio stations.
- */
- public static final DecimalFormat FM_FORMATTER = new DecimalFormat(FM_CHANNEL_FORMAT);
-
- /**
- * The formatter for FM radio stations.
- */
- public static final DecimalFormat AM_FORMATTER = new DecimalFormat(AM_CHANNEL_FORMAT);
-
- /**
- * Convenience method to format a given {@link RadioStation} based on the value in
- * {@link RadioStation#getRadioBand()}. If the band is invalid or support for its formatting is
- * not available, then an empty String is returned.
- *
- * @param band One of the band values specified in {@link RadioManager}. For example,
- * {@link RadioManager#BAND_FM}.
- * @param channelNumber The channel number to format. This value should be in KHz.
- * @return A correctly formatted channel number or an empty string if one cannot be formed.
- */
- public static String formatRadioChannel(int band, int channelNumber) {
- switch (band) {
- case RadioManager.BAND_AM:
- return AM_FORMATTER.format(channelNumber);
-
- case RadioManager.BAND_FM:
- // FM channels are displayed in KHz, so divide by 1000.
- return FM_FORMATTER.format((float) channelNumber / 1000);
-
- default:
- return "";
- }
- }
-
- /**
- * Formats the given band value into a readable String.
- *
- * @param band One of the band values specified in {@link RadioManager}. For example,
- * {@link RadioManager#BAND_FM}.
- * @return The formatted string or an empty string if the band is invalid.
- */
- public static String formatRadioBand(Context context, int band) {
- String radioBandText;
-
- switch (band) {
- case RadioManager.BAND_AM:
- radioBandText = context.getString(R.string.radio_am_text);
- break;
-
- case RadioManager.BAND_FM:
- radioBandText = context.getString(R.string.radio_fm_text);
- break;
-
- default:
- radioBandText = "";
- }
-
- return radioBandText.toUpperCase(Locale.getDefault());
- }
-}
diff --git a/src/com/android/car/stream/radio/RadioStreamProducer.java b/src/com/android/car/stream/radio/RadioStreamProducer.java
deleted file mode 100644
index 4c36650..0000000
--- a/src/com/android/car/stream/radio/RadioStreamProducer.java
+++ /dev/null
@@ -1,326 +0,0 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
- *
- * 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.android.car.stream.radio;
-
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.ServiceConnection;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Log;
-import com.android.car.radio.service.IRadioCallback;
-import com.android.car.radio.service.IRadioManager;
-import com.android.car.radio.service.RadioRds;
-import com.android.car.radio.service.RadioStation;
-import com.android.car.stream.R;
-import com.android.car.stream.StreamProducer;
-
-/**
- * A {@link StreamProducer} that will connect to the {@link IRadioManager} and produce cards
- * corresponding to the currently playing radio station.
- */
-public class RadioStreamProducer extends StreamProducer {
- private static final String TAG = "RadioStreamProducer";
-
- /**
- * The amount of time to wait before re-trying to connect to {@link IRadioManager}.
- */
- private static final int SERVICE_CONNECTION_RETRY_DELAY_MS = 5000;
-
- // Radio actions that are used by broadcasts that occur on interaction with the radio card.
- static final int ACTION_SEEK_FORWARD = 1;
- static final int ACTION_SEEK_BACKWARD = 2;
- static final int ACTION_PAUSE = 3;
- static final int ACTION_PLAY = 4;
- static final int ACTION_STOP = 5;
-
- /**
- * The action in an {@link Intent} that is meant to effect certain radio actions.
- */
- static final String RADIO_INTENT_ACTION =
- "com.android.car.stream.radio.RADIO_INTENT_ACTION";
-
- /**
- * The extra within the {@link Intent} that points to the specific action to be taken on the
- * radio.
- */
- static final String RADIO_ACTION_EXTRA = "radio_action_extra";
-
- private final Handler mHandler = new Handler();
-
- private IRadioManager mRadioManager;
- private RadioActionReceiver mReceiver;
- private final RadioConverter mConverter;
-
- /**
- * The number of times that this stream producer has attempted to reconnect to the
- * {@link IRadioManager} after a failure to bind.
- */
- private int mConnectionRetryCount;
-
- private int mCurrentChannelNumber;
- private int mCurrentBand;
-
- public RadioStreamProducer(Context context) {
- super(context);
- mConverter = new RadioConverter(context);
- }
-
- @Override
- public void start() {
- super.start();
-
- mReceiver = new RadioActionReceiver();
- mContext.registerReceiver(mReceiver, new IntentFilter(RADIO_INTENT_ACTION));
-
- bindRadioService();
- }
-
- @Override
- public void stop() {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "stop()");
- }
-
- mHandler.removeCallbacks(mServiceConnectionRetry);
-
- mContext.unregisterReceiver(mReceiver);
- mReceiver = null;
-
- mContext.unbindService(mServiceConnection);
- super.stop();
- }
-
- /**
- * Binds to the RadioService and returns {@code true} if the connection was successful.
- */
- private boolean bindRadioService() {
- Intent radioService = new Intent();
- radioService.setComponent(new ComponentName(
- mContext.getString(R.string.car_radio_component_package),
- mContext.getString(R.string.car_radio_component_service)));
-
- boolean bound =
- !mContext.bindService(radioService, mServiceConnection, Context.BIND_AUTO_CREATE);
-
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "bindRadioService(). Connected to radio service: " + bound);
- }
-
- return bound;
- }
-
- /**
- * A {@link BroadcastReceiver} that listens for Intents that have the action
- * {@link #RADIO_INTENT_ACTION} and corresponding parses the action event within it to effect
- * radio playback.
- */
- private class RadioActionReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (mRadioManager == null || !RADIO_INTENT_ACTION.equals(intent.getAction())) {
- return;
- }
-
- int radioAction = intent.getIntExtra(RADIO_ACTION_EXTRA, -1);
- if (radioAction == -1) {
- return;
- }
-
- switch (radioAction) {
- case ACTION_SEEK_FORWARD:
- try {
- mRadioManager.seekForward();
- } catch (RemoteException e) {
- Log.e(TAG, "Seek forward exception: " + e.getMessage());
- }
- break;
-
- case ACTION_SEEK_BACKWARD:
- try {
- mRadioManager.seekBackward();
- } catch (RemoteException e) {
- Log.e(TAG, "Seek backward exception: " + e.getMessage());
- }
- break;
-
- case ACTION_PLAY:
- try {
- mRadioManager.unMute();
- } catch (RemoteException e) {
- Log.e(TAG, "Radio play exception: " + e.getMessage());
- }
- break;
-
- case ACTION_STOP:
- case ACTION_PAUSE:
- try {
- mRadioManager.mute();
- } catch (RemoteException e) {
- Log.e(TAG, "Radio pause exception: " + e.getMessage());
- }
- break;
-
- default:
- // Do nothing.
- }
- }
- }
-
- /**
- * A {@link IRadioCallback} that will be notified of various state changes in the radio station.
- * Upon these changes, it will push a new {@link com.android.car.stream.StreamCard} to the
- * Stream service.
- */
- private final IRadioCallback.Stub mCallback = new IRadioCallback.Stub() {
- @Override
- public void onRadioStationChanged(RadioStation station) {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "onRadioStationChanged: " + station);
- }
-
- mCurrentBand = station.getRadioBand();
- mCurrentChannelNumber = station.getChannelNumber();
-
- if (mRadioManager == null) {
- return;
- }
-
- try {
- boolean isPlaying = !mRadioManager.isMuted();
- postCard(mConverter.convert(station, isPlaying));
- } catch (RemoteException e) {
- Log.e(TAG, "Post radio station changed error: " + e.getMessage());
- }
- }
-
- @Override
- public void onRadioMetadataChanged(RadioRds rds) {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "onRadioMetadataChanged: " + rds);
- }
-
- // Ignore metadata changes because this will overwhelm the notifications. Instead,
- // Only display the metadata that is retrieved in onRadioStationChanged().
- }
-
- @Override
- public void onRadioBandChanged(int radioBand) {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "onRadioBandChanged: " + radioBand);
- }
-
- if (mRadioManager == null) {
- return;
- }
-
- try {
- RadioStation station = new RadioStation(mCurrentChannelNumber,
- 0 /* subChannelNumber */, mCurrentBand, null /* rds */);
- boolean isPlaying = !mRadioManager.isMuted();
-
- postCard(mConverter.convert(station, isPlaying));
- } catch (RemoteException e) {
- Log.e(TAG, "Post radio station changed error: " + e.getMessage());
- }
- }
-
- @Override
- public void onRadioMuteChanged(boolean isMuted) {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "onRadioMuteChanged(): " + isMuted);
- }
-
- RadioStation station = new RadioStation(mCurrentChannelNumber,
- 0 /* subChannelNumber */, mCurrentBand, null /* rds */);
-
- postCard(mConverter.convert(station, !isMuted));
- }
-
- @Override
- public void onError(int status) {
- Log.e(TAG, "Radio error: " + status);
- }
- };
-
- private ServiceConnection mServiceConnection = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder binder) {
- mConnectionRetryCount = 0;
-
- mRadioManager = IRadioManager.Stub.asInterface(binder);
-
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "onSeviceConnected(): " + mRadioManager);
- }
-
- try {
- mRadioManager.addRadioTunerCallback(mCallback);
-
- if (mRadioManager.isInitialized() && mRadioManager.hasFocus()) {
- boolean isPlaying = !mRadioManager.isMuted();
- postCard(mConverter.convert(mRadioManager.getCurrentRadioStation(), isPlaying));
- }
- } catch (RemoteException e) {
- Log.e(TAG, "addRadioTunerCallback() error: " + e.getMessage());
- }
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "onServiceDisconnected(): " + name);
- }
- mRadioManager = null;
-
- // If the service has been disconnected, attempt to reconnect.
- mHandler.removeCallbacks(mServiceConnectionRetry);
- mHandler.postDelayed(mServiceConnectionRetry, SERVICE_CONNECTION_RETRY_DELAY_MS);
- }
- };
-
- /**
- * A {@link Runnable} that is responsible for attempting to reconnect to {@link IRadioManager}.
- */
- private Runnable mServiceConnectionRetry = new Runnable() {
- @Override
- public void run() {
- if (mRadioManager != null) {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "RadioService rebound by framework, no need to bind again");
- }
- return;
- }
-
- mConnectionRetryCount++;
-
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Rebinding disconnected RadioService, retry count: "
- + mConnectionRetryCount);
- }
-
- if (!bindRadioService()) {
- mHandler.postDelayed(mServiceConnectionRetry,
- mConnectionRetryCount * SERVICE_CONNECTION_RETRY_DELAY_MS);
- }
- }
- };
-}
diff --git a/src/com/android/car/stream/telecom/CurrentCallConverter.java b/src/com/android/car/stream/telecom/CurrentCallConverter.java
deleted file mode 100644
index 8c29919..0000000
--- a/src/com/android/car/stream/telecom/CurrentCallConverter.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
- *
- * 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.android.car.stream.telecom;
-
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.Bitmap;
-import android.graphics.drawable.VectorDrawable;
-import android.telecom.Call;
-import com.android.car.stream.BitmapUtils;
-import com.android.car.stream.CurrentCallExtension;
-import com.android.car.stream.R;
-import com.android.car.stream.StreamCard;
-import com.android.car.stream.StreamConstants;
-
-/**
- * A converter that creates a {@link StreamCard} for the current call events.
- */
-public class CurrentCallConverter {
- private static final int MUTE_BUTTON_REQUEST_CODE = 12;
- private static final int CALL_BUTTON_REQUEST_CODE = 13;
-
- private PendingIntent mMuteAction;
- private PendingIntent mUnMuteAction;
- private PendingIntent mAcceptCallAction;
- private PendingIntent mHangupCallAction;
-
- public CurrentCallConverter(Context context) {
- mMuteAction = getCurrentCallAction(context,
- TelecomConstants.ACTION_MUTE, MUTE_BUTTON_REQUEST_CODE);
- mUnMuteAction = getCurrentCallAction(context,
- TelecomConstants.ACTION_MUTE, MUTE_BUTTON_REQUEST_CODE);
-
- mAcceptCallAction = getCurrentCallAction(context,
- TelecomConstants.ACTION_ACCEPT_CALL, CALL_BUTTON_REQUEST_CODE);
- mHangupCallAction = getCurrentCallAction(context,
- TelecomConstants.ACTION_HANG_UP_CALL, CALL_BUTTON_REQUEST_CODE);
- }
-
- private PendingIntent getCurrentCallAction(Context context,
- String action, int requestcode) {
- Intent intent = new Intent(TelecomConstants.INTENT_ACTION_STREAM_CALL_CONTROL);
- intent.setPackage(context.getPackageName());
- intent.putExtra(TelecomConstants.EXTRA_STREAM_CALL_ACTION, action);
- PendingIntent pendingIntent =
- PendingIntent.getBroadcast(
- context,
- requestcode,
- intent,
- PendingIntent.FLAG_CANCEL_CURRENT
- );
- return pendingIntent;
- }
-
- public StreamCard convert(Call call, Context context, boolean isMuted,
- long callStartTime, String dialerPackage) {
- long timeStamp = System.currentTimeMillis() - call.getDetails().getConnectTimeMillis();
- int callState = call.getState();
- String number = TelecomUtils.getNumber(call);
- String displayName = TelecomUtils.getDisplayName(context, call);
- long digits = Long.valueOf(number.replaceAll("[^0-9]", ""));
-
- PendingIntent dialerPendingIntent =
- PendingIntent.getActivity(
- context,
- 0,
- context.getPackageManager().getLaunchIntentForPackage(dialerPackage),
- PendingIntent.FLAG_UPDATE_CURRENT
- );
-
- StreamCard.Builder builder = new StreamCard.Builder(StreamConstants.CARD_TYPE_CURRENT_CALL,
- digits /* id */, timeStamp);
- builder.setPrimaryText(displayName);
- builder.setSecondaryText(getCallState(context, callState));
-
- Bitmap phoneIcon = BitmapUtils.getBitmap(
- (VectorDrawable) context.getDrawable(R.drawable.ic_phone));
- builder.setPrimaryIcon(phoneIcon);
- builder.setSecondaryIcon(TelecomUtils.createStreamCardSecondaryIcon(context, number));
- builder.setClickAction(dialerPendingIntent);
- builder.setCardExtension(createCurrentCallExtension(context, callStartTime, displayName,
- callState, isMuted, number));
- return builder.build();
- }
-
- private CurrentCallExtension createCurrentCallExtension(Context context, long callStartTime,
- String displayName, int callState, boolean isMuted, String number) {
-
- Bitmap contactPhoto = TelecomUtils
- .getContactPhotoFromNumber(context.getContentResolver(), number);
- CurrentCallExtension extension
- = new CurrentCallExtension(callStartTime, displayName, callState, isMuted,
- contactPhoto, mMuteAction, mUnMuteAction, mAcceptCallAction, mHangupCallAction);
- return extension;
- }
-
- private String getCallState(Context context, int state) {
- switch (state) {
- case Call.STATE_ACTIVE:
- return context.getString(R.string.ongoing_call);
- case Call.STATE_DIALING:
- return context.getString(R.string.dialing_call);
- case Call.STATE_DISCONNECTING:
- return context.getString(R.string.disconnecting_call);
- case Call.STATE_RINGING:
- return context.getString(R.string.notification_incoming_call);
- default:
- return context.getString(R.string.unknown);
- }
- }
-
-}
diff --git a/src/com/android/car/stream/telecom/CurrentCallStreamProducer.java b/src/com/android/car/stream/telecom/CurrentCallStreamProducer.java
deleted file mode 100644
index 2b774b7..0000000
--- a/src/com/android/car/stream/telecom/CurrentCallStreamProducer.java
+++ /dev/null
@@ -1,234 +0,0 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
- *
- * 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.android.car.stream.telecom;
-
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.ServiceConnection;
-import android.os.AsyncTask;
-import android.os.IBinder;
-import android.os.SystemClock;
-import android.telecom.Call;
-import android.telecom.CallAudioState;
-import android.telecom.TelecomManager;
-import android.util.Log;
-import com.android.car.stream.StreamCard;
-import com.android.car.stream.StreamProducer;
-import com.android.car.stream.telecom.StreamInCallService.StreamInCallServiceBinder;
-
-/**
- * A {@link StreamProducer} that listens for active call events and produces a {@link StreamCard}
- */
-public class CurrentCallStreamProducer extends StreamProducer
- implements StreamInCallService.InCallServiceCallback {
- private static final String TAG = "CurrentCallProducer";
-
- private StreamInCallService mInCallService;
- private PhoneCallback mPhoneCallback;
- private CurrentCallActionReceiver mCallActionReceiver;
- private Call mCurrentCall;
- private long mCurrentCallStartTime;
-
- private CurrentCallConverter mConverter;
- private AsyncTask mUpdateStreamItemTask;
-
- private String mDialerPackage;
- private TelecomManager mTelecomManager;
-
- public CurrentCallStreamProducer(Context context) {
- super(context);
- }
-
- @Override
- public void start() {
- super.start();
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "current call producer started");
- }
- mTelecomManager = (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
- mDialerPackage = mTelecomManager.getDefaultDialerPackage();
- mConverter = new CurrentCallConverter(mContext);
- mPhoneCallback = new PhoneCallback();
-
- Intent inCallServiceIntent = new Intent(mContext, StreamInCallService.class);
- inCallServiceIntent.setAction(StreamInCallService.LOCAL_INCALL_SERVICE_BIND_ACTION);
- mContext.bindService(inCallServiceIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
- }
-
- @Override
- public void stop() {
- mContext.unbindService(mServiceConnection);
- super.stop();
- }
-
- private void acceptCall() {
- synchronized (mTelecomManager) {
- if (mCurrentCall != null && mCurrentCall.getState() == Call.STATE_RINGING) {
- mCurrentCall.answer(0 /* videoState */);
- }
- }
- }
-
- private void disconnectCall() {
- synchronized (mTelecomManager) {
- if (mCurrentCall != null) {
- mCurrentCall.disconnect();
- }
- }
- }
-
- @Override
- public void onCallAdded(Call call) {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "on call added, state: " + call.getState());
- }
- mCurrentCall = call;
- updateStreamCard(mCurrentCall, mContext);
- call.registerCallback(mPhoneCallback);
- }
-
- @Override
- public void onCallRemoved(Call call) {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "on call removed, state: " + call.getState());
- }
- call.unregisterCallback(mPhoneCallback);
- updateStreamCard(call, mContext);
- mCurrentCall = null;
- }
-
- @Override
- public void onCallAudioStateChanged(CallAudioState audioState) {
- if (mCurrentCall != null && audioState != null) {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "audio state changed, is muted? " + audioState.isMuted());
- }
- updateStreamCard(mCurrentCall, mContext);
- }
- }
-
- private void clearUpdateStreamItemTask() {
- if (mUpdateStreamItemTask != null) {
- mUpdateStreamItemTask.cancel(false);
- mUpdateStreamItemTask = null;
- }
- }
-
- private void updateStreamCard(final Call call, final Context context) {
- // Only one update may be active at a time.
- clearUpdateStreamItemTask();
-
- mUpdateStreamItemTask = new AsyncTask<Void, Void, StreamCard>() {
- @Override
- protected StreamCard doInBackground(Void... voids) {
- try {
- return mConverter.convert(call, context, mInCallService.isMuted(),
- mCurrentCallStartTime, mDialerPackage);
- } catch (Exception e) {
- Log.e(TAG, "Failed to create StreamItem.", e);
- throw e;
- }
- }
-
- @Override
- protected void onPostExecute(StreamCard card) {
- if (call.getState() == Call.STATE_DISCONNECTED) {
- removeCard(card);
- } else {
- postCard(card);
- }
- }
- }.execute();
- }
-
- private class PhoneCallback extends Call.Callback {
- @Override
- public void onStateChanged(Call call, int state) {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "onStateChanged call: " + call + ", state: " + state);
- }
-
- if (state == Call.STATE_ACTIVE) {
- mCurrentCallStartTime = SystemClock.elapsedRealtime();
- } else {
- mCurrentCallStartTime = 0;
- }
-
- switch (state) {
- // TODO: Determine if a HUD or stream card should be displayed.
- case Call.STATE_RINGING: // Incoming call is ringing.
- case Call.STATE_DIALING: // Outgoing call that is dialing.
- case Call.STATE_ACTIVE: // Call is connected
- case Call.STATE_DISCONNECTING: // Call is being disconnected
- case Call.STATE_DISCONNECTED: // Call has finished.
- updateStreamCard(call, mContext);
- mCurrentCall = call;
- break;
- default:
- }
- }
- }
-
- private class CurrentCallActionReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- String intentAction = intent.getAction();
- if (!TelecomConstants.INTENT_ACTION_STREAM_CALL_CONTROL.equals(intentAction)) {
- return;
- }
-
- String action = intent.getStringExtra(TelecomConstants.EXTRA_STREAM_CALL_ACTION);
- switch (action) {
- case TelecomConstants.ACTION_MUTE:
- mInCallService.setMuted(true);
- break;
- case TelecomConstants.ACTION_UNMUTE:
- mInCallService.setMuted(false);
- break;
- case TelecomConstants.ACTION_ACCEPT_CALL:
- acceptCall();
- break;
- case TelecomConstants.ACTION_HANG_UP_CALL:
- disconnectCall();
- break;
- default:
- }
- }
- }
-
- private ServiceConnection mServiceConnection = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- StreamInCallServiceBinder binder = (StreamInCallServiceBinder) service;
- mInCallService = binder.getService();
- mInCallService.setCallback(CurrentCallStreamProducer.this);
-
- if (mCallActionReceiver == null) {
- mCallActionReceiver = new CurrentCallActionReceiver();
- mContext.registerReceiver(mCallActionReceiver,
- new IntentFilter(TelecomConstants.INTENT_ACTION_STREAM_CALL_CONTROL));
- }
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- mInCallService = null;
- }
- };
-}
diff --git a/src/com/android/car/stream/telecom/RecentCallConverter.java b/src/com/android/car/stream/telecom/RecentCallConverter.java
deleted file mode 100644
index 77bcec8..0000000
--- a/src/com/android/car/stream/telecom/RecentCallConverter.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
- *
- * 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.android.car.stream.telecom;
-
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.Bitmap;
-import android.graphics.drawable.VectorDrawable;
-import android.net.Uri;
-import com.android.car.stream.BitmapUtils;
-import com.android.car.stream.R;
-import com.android.car.stream.StreamCard;
-import com.android.car.stream.StreamConstants;
-
-public class RecentCallConverter {
-
- /**
- * Creates a StreamCard of type {@link StreamConstants#CARD_TYPE_RECENT_CALL}
- * @return
- */
- public StreamCard createStreamCard(Context context, String number, long timestamp) {
- StreamCard.Builder builder = new StreamCard.Builder(StreamConstants.CARD_TYPE_RECENT_CALL,
- Long.parseLong(number), timestamp);
- String displayName = TelecomUtils.getDisplayName(context, number);
-
- builder.setPrimaryText(displayName);
- builder.setSecondaryText(context.getString(R.string.recent_call));
- builder.setDescription(context.getString(R.string.recent_call));
- Bitmap phoneIcon = BitmapUtils.getBitmap(
- (VectorDrawable) context.getDrawable(R.drawable.ic_phone));
-
- builder.setPrimaryIcon(phoneIcon);
- builder.setSecondaryIcon(TelecomUtils.createStreamCardSecondaryIcon(context, number));
- builder.setClickAction(createCallPendingIntent(context, number));
- return builder.build();
- }
-
- private PendingIntent createCallPendingIntent(Context context, String number) {
- Intent callIntent = new Intent(Intent.ACTION_DIAL);
- callIntent.setData(Uri.parse("tel: " + number));
- callIntent.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT);
- return PendingIntent.getActivity(context, 0, callIntent,
- PendingIntent.FLAG_UPDATE_CURRENT);
- }
-}
diff --git a/src/com/android/car/stream/telecom/RecentCallStreamProducer.java b/src/com/android/car/stream/telecom/RecentCallStreamProducer.java
deleted file mode 100644
index 9581226..0000000
--- a/src/com/android/car/stream/telecom/RecentCallStreamProducer.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
- *
- * 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.android.car.stream.telecom;
-
-import android.content.Context;
-import android.content.CursorLoader;
-import android.content.Loader;
-import android.content.pm.PackageManager;
-import android.database.Cursor;
-import android.net.Uri;
-import android.provider.CallLog;
-import android.text.TextUtils;
-import android.text.format.DateUtils;
-import android.util.Log;
-import com.android.car.stream.StreamCard;
-import com.android.car.stream.StreamProducer;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Loads recent calls from the call log and produces a {@link StreamCard} for each entry.
- */
-public class RecentCallStreamProducer extends StreamProducer
- implements Loader.OnLoadCompleteListener<Cursor> {
- private static final String TAG = "RecentCallProducer";
- private static final long RECENT_CALL_TIME_RANGE = 6 * DateUtils.HOUR_IN_MILLIS;
-
- /** Number of call log items to query for */
- private static final int CALL_LOG_QUERY_LIMIT = 1;
- private static final String[] EMPTY_STRING_ARRAY = new String[0];
-
- private CursorLoader mCursorLoader;
- private StreamCard mCurrentStreamCard;
- private long mCurrentNumber;
- private RecentCallConverter mConverter = new RecentCallConverter();
-
- public RecentCallStreamProducer(Context context) {
- super(context);
- mCursorLoader = createCallLogLoader();
- }
-
- @Override
- public void start() {
- super.start();
- if (!hasReadCallLogPermission()) {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Could not onStart RecentCallStreamProducer, permissions not granted");
- }
- return;
- }
-
- if (!mCursorLoader.isStarted()) {
- mCursorLoader.startLoading();
- }
- }
-
- @Override
- public void stop() {
- if (mCursorLoader.isStarted()) {
- mCursorLoader.stopLoading();
- removeCard(mCurrentStreamCard);
- mCurrentStreamCard = null;
- mCurrentNumber = 0;
- }
- super.stop();
- }
-
- @Override
- public void onLoadComplete(Loader<Cursor> loader, Cursor cursor) {
- if (cursor == null || !cursor.moveToFirst()) {
- return;
- }
-
- int column = cursor.getColumnIndex(CallLog.Calls.NUMBER);
- String number = cursor.getString(column);
- column = cursor.getColumnIndex(CallLog.Calls.DATE);
- long callTimeMs = cursor.getLong(column);
- // Display if we have a phone number, and the call was within 6hours.
- number = number.replaceAll("[^0-9]", "");
- long timestamp = System.currentTimeMillis();
- long digits = Long.parseLong(number);
-
- if (!TextUtils.isEmpty(number) &&
- (timestamp - callTimeMs) < RECENT_CALL_TIME_RANGE) {
- if (mCurrentStreamCard == null || mCurrentNumber != digits) {
- removeCard(mCurrentStreamCard);
- mCurrentStreamCard = mConverter.createStreamCard(mContext, number, timestamp);
- mCurrentNumber = digits;
- postCard(mCurrentStreamCard);
- }
- }
- }
-
- private boolean hasReadCallLogPermission() {
- return mContext.checkSelfPermission(android.Manifest.permission.READ_CALL_LOG)
- == PackageManager.PERMISSION_GRANTED;
- }
-
- /**
- * Creates a CursorLoader for Call data.
- * Note: NOT to be used with LoaderManagers.
- */
- private CursorLoader createCallLogLoader() {
- // We need to check for NULL explicitly otherwise entries with where READ is NULL
- // may not match either the query or its negation.
- // We consider the calls that are not yet consumed (i.e. IS_READ = 0) as "new".
- StringBuilder where = new StringBuilder();
- List<String> selectionArgs = new ArrayList<String>();
-
- String selection = where.length() > 0 ? where.toString() : null;
- Uri uri = CallLog.Calls.CONTENT_URI.buildUpon()
- .appendQueryParameter(CallLog.Calls.LIMIT_PARAM_KEY,
- Integer.toString(CALL_LOG_QUERY_LIMIT))
- .build();
- CursorLoader loader = new CursorLoader(mContext, uri, null, selection,
- selectionArgs.toArray(EMPTY_STRING_ARRAY), CallLog.Calls.DEFAULT_SORT_ORDER);
- loader.registerListener(0, this /* OnLoadCompleteListener */);
- return loader;
- }
-
-}
diff --git a/src/com/android/car/stream/telecom/StreamInCallService.java b/src/com/android/car/stream/telecom/StreamInCallService.java
deleted file mode 100644
index fef7155..0000000
--- a/src/com/android/car/stream/telecom/StreamInCallService.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
- *
- * 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.android.car.stream.telecom;
-
-import android.content.Intent;
-import android.os.Binder;
-import android.os.IBinder;
-import android.telecom.Call;
-import android.telecom.CallAudioState;
-import android.telecom.InCallService;
-import android.util.Log;
-
-/**
- * {@link InCallService} to listen for incoming calls and changes in call state.
- */
-public class StreamInCallService extends InCallService {
- public static final String LOCAL_INCALL_SERVICE_BIND_ACTION = "stream_incall_service_action";
- private static final String TAG = "StreamInCallService";
- private final IBinder mBinder = new StreamInCallServiceBinder();
-
- private InCallServiceCallback mCallback;
-
- /**
- * Callback interface to receive changes in the call state.
- */
- public interface InCallServiceCallback {
- void onCallAdded(Call call);
-
- void onCallRemoved(Call call);
-
- void onCallAudioStateChanged(CallAudioState audioState);
- }
-
- public class StreamInCallServiceBinder extends Binder {
- StreamInCallService getService() {
- return StreamInCallService.this;
- }
- }
-
- public void setCallback(InCallServiceCallback callback) {
- mCallback = callback;
- }
-
- @Override
- public IBinder onBind(Intent intent) {
- // This service can be bound by the framework or a local stream producer.
- // Check the action and return the appropriate IBinder.
- if (LOCAL_INCALL_SERVICE_BIND_ACTION.equals(intent.getAction())) {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "onBind with action: LOCAL_INCALL_SERVICE_BIND_ACTION," +
- " returning StreamInCallServiceBinder");
- }
- return mBinder;
- }
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "onBind without action specified, returning InCallService");
- }
- return super.onBind(intent);
- }
-
- @Override
- public void onCallAdded(Call call) {
- if (mCallback != null) {
- mCallback.onCallAdded(call);
- }
- }
-
- @Override
- public void onCallRemoved(Call call) {
- if (mCallback != null) {
- mCallback.onCallRemoved(call);
- }
- }
-
- @Override
- public void onCallAudioStateChanged(CallAudioState audioState) {
- if (mCallback != null) {
- mCallback.onCallAudioStateChanged(audioState);
- }
- super.onCallAudioStateChanged(audioState);
- }
-
- public boolean isMuted() {
- CallAudioState audioState = getCallAudioState();
- return audioState != null && audioState.isMuted();
- }
-}
diff --git a/src/com/android/car/stream/telecom/TelecomConstants.java b/src/com/android/car/stream/telecom/TelecomConstants.java
deleted file mode 100644
index c5189fc..0000000
--- a/src/com/android/car/stream/telecom/TelecomConstants.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
- *
- * 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.android.car.stream.telecom;
-
-public class TelecomConstants {
- public static final String INTENT_ACTION_STREAM_CALL_CONTROL
- = "com.google.android.car.stream.telecom.CALL_CONTROL";
- public static final String EXTRA_STREAM_CALL_ACTION
- = "com.google.android.car.stream.telecom.ACTION";
-
- public static final String ACTION_MUTE = "mute";
- public static final String ACTION_UNMUTE = "unmute";
- public static final String ACTION_HANG_UP_CALL = "hang_up_call";
- public static final String ACTION_ACCEPT_CALL = "accept_call";
-}
diff --git a/src/com/android/car/stream/telecom/TelecomUtils.java b/src/com/android/car/stream/telecom/TelecomUtils.java
deleted file mode 100644
index 3d56e70..0000000
--- a/src/com/android/car/stream/telecom/TelecomUtils.java
+++ /dev/null
@@ -1,359 +0,0 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
- *
- * 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.android.car.stream.telecom;
-
-import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.Context;
-import android.content.res.Resources;
-import android.database.Cursor;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Rect;
-import android.net.Uri;
-import android.provider.ContactsContract;
-import android.support.annotation.Nullable;
-import android.support.annotation.WorkerThread;
-import android.telecom.Call;
-import android.telecom.GatewayInfo;
-import android.telephony.PhoneNumberUtils;
-import android.telephony.TelephonyManager;
-import android.text.TextUtils;
-import android.util.LruCache;
-import com.android.car.apps.common.CircleBitmapDrawable;
-import com.android.car.apps.common.LetterTileDrawable;
-import com.android.car.stream.R;
-
-import java.io.InputStream;
-import java.util.HashMap;
-import java.util.Locale;
-
-/**
- * Telecom related utility methods.
- */
-public class TelecomUtils {
- private static final int LRU_CACHE_SIZE = 4194304; /** 4 mb **/
-
- private static final String[] CONTACT_ID_PROJECTION = new String[] {
- ContactsContract.PhoneLookup.DISPLAY_NAME,
- ContactsContract.PhoneLookup.TYPE,
- ContactsContract.PhoneLookup.LABEL,
- ContactsContract.PhoneLookup._ID
- };
-
- private static String sVoicemailNumber;
-
- private static LruCache<String, Bitmap> sContactPhotoNumberCache;
- private static LruCache<Long, Bitmap> sContactPhotoIdCache;
- private static HashMap<String, String> sContactNameCache;
- private static HashMap<String, Integer> sContactIdCache;
- private static HashMap<String, String> sFormattedNumberCache;
- private static HashMap<String, String> sDisplayNameCache;
-
- /**
- * Create a round bitmap icon to represent the call. If a contact photo does not exist,
- * a letter tile will be used instead.
- */
- public static Bitmap createStreamCardSecondaryIcon(Context context, String number) {
- Resources res = context.getResources();
- Bitmap largeIcon
- = TelecomUtils.getContactPhotoFromNumber(context.getContentResolver(), number);
- if (largeIcon == null) {
- LetterTileDrawable ltd = new LetterTileDrawable(res);
- String name = TelecomUtils.getDisplayName(context, number);
- ltd.setContactDetails(name, number);
- ltd.setIsCircular(true);
- int size = res.getDimensionPixelSize(R.dimen.stream_card_secondary_icon_dimen);
- largeIcon = ltd.toBitmap(size);
- }
-
- return new CircleBitmapDrawable(res, largeIcon)
- .toBitmap(res.getDimensionPixelSize(R.dimen.stream_card_secondary_icon_dimen));
- }
-
-
- /**
- * Fetch contact photo by number from local cache.
- *
- * @param number
- * @return Contact photo if it's in the cache, otherwise null.
- */
- @Nullable
- public static Bitmap getCachedContactPhotoFromNumber(String number) {
- if (number == null) {
- return null;
- }
-
- if (sContactPhotoNumberCache == null) {
- sContactPhotoNumberCache = new LruCache<String, Bitmap>(LRU_CACHE_SIZE) {
- @Override
- protected int sizeOf(String key, Bitmap value) {
- return value.getByteCount();
- }
- };
- }
- return sContactPhotoNumberCache.get(number);
- }
-
- @WorkerThread
- public static Bitmap getContactPhotoFromNumber(ContentResolver contentResolver, String number) {
- if (number == null) {
- return null;
- }
-
- Bitmap photo = getCachedContactPhotoFromNumber(number);
- if (photo != null) {
- return photo;
- }
-
- int id = getContactIdFromNumber(contentResolver, number);
- if (id == 0) {
- return null;
- }
- photo = getContactPhotoFromId(contentResolver, id);
- if (photo != null) {
- sContactPhotoNumberCache.put(number, photo);
- }
- return photo;
- }
-
- /**
- * Return the contact id for the given contact id
- * @param id the contact id to get the photo for
- * @return the contact photo if it is found, null otherwise.
- */
- public static Bitmap getContactPhotoFromId(ContentResolver contentResolver, long id) {
- if (sContactPhotoIdCache == null) {
- sContactPhotoIdCache = new LruCache<Long, Bitmap>(LRU_CACHE_SIZE) {
- @Override
- protected int sizeOf(Long key, Bitmap value) {
- return value.getByteCount();
- }
- };
- } else if (sContactPhotoIdCache.get(id) != null) {
- return sContactPhotoIdCache.get(id);
- }
-
- Uri photoUri = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, id);
- InputStream photoDataStream = ContactsContract.Contacts.openContactPhotoInputStream(
- contentResolver, photoUri, true);
-
- BitmapFactory.Options options = new BitmapFactory.Options();
- options.inPreferQualityOverSpeed = true;
- // Scaling will be handled by later. We shouldn't scale multiple times to avoid
- // quality lost due to multiple potential scaling up and down.
- options.inScaled = false;
-
- Rect nullPadding = null;
- Bitmap photo = BitmapFactory.decodeStream(photoDataStream, nullPadding, options);
- if (photo != null) {
- photo.setDensity(Bitmap.DENSITY_NONE);
- sContactPhotoIdCache.put(id, photo);
- }
- return photo;
- }
-
- /**
- * Return the contact id for the given phone number.
- * @param number Caller phone number
- * @return the contact id if it is found, 0 otherwise.
- */
- public static int getContactIdFromNumber(ContentResolver cr, String number) {
- if (number == null || number.isEmpty()) {
- return 0;
- }
- if (sContactIdCache == null) {
- sContactIdCache = new HashMap<>();
- } else if (sContactIdCache.containsKey(number)) {
- return sContactIdCache.get(number);
- }
-
- Uri uri = Uri.withAppendedPath(
- ContactsContract.PhoneLookup.CONTENT_FILTER_URI,
- Uri.encode(number));
- Cursor cursor = cr.query(uri, CONTACT_ID_PROJECTION, null, null, null);
-
- try {
- if (cursor != null && cursor.moveToFirst()) {
- int id = cursor.getInt(cursor.getColumnIndex(ContactsContract.PhoneLookup._ID));
- sContactIdCache.put(number, id);
- return id;
- }
- }
- finally {
- if (cursor != null) {
- cursor.close();
- }
- }
- return 0;
- }
-
- public static String getDisplayName(Context context, String number) {
- return getDisplayName(context, number, (Uri)null);
- }
-
- public static String getDisplayName(Context context, Call call) {
- // A call might get created before its children are added. In that case, the display name
- // would go from "Unknown" to "Conference call" therefore we don't want to cache it.
- if (call.getChildren() != null && call.getChildren().size() > 0) {
- return context.getString(R.string.conference_call);
- }
- return getDisplayName(context, getNumber(call), getGatewayInfoOriginalAddress(call));
- }
-
- private static Uri getGatewayInfoOriginalAddress(Call call) {
- if (call == null || call.getDetails() == null) {
- return null;
- }
- GatewayInfo gatewayInfo = call.getDetails().getGatewayInfo();
-
- if (gatewayInfo != null && gatewayInfo.getOriginalAddress() != null) {
- return gatewayInfo.getGatewayAddress();
- }
- return null;
- }
-
- /**
- * Return the phone number of the call. This CAN return null under certain circumstances such
- * as if the incoming number is hidden.
- */
- public static String getNumber(Call call) {
- if (call == null || call.getDetails() == null) {
- return null;
- }
-
- Uri gatewayInfoOriginalAddress = getGatewayInfoOriginalAddress(call);
- if (gatewayInfoOriginalAddress != null) {
- return gatewayInfoOriginalAddress.getSchemeSpecificPart();
- }
-
- if (call.getDetails().getHandle() != null) {
- return call.getDetails().getHandle().getSchemeSpecificPart();
- }
- return null;
- }
-
- private static String getContactNameFromNumber(ContentResolver cr, String number) {
- if (sContactNameCache == null) {
- sContactNameCache = new HashMap<>();
- } else if (sContactNameCache.containsKey(number)) {
- return sContactNameCache.get(number);
- }
-
- Uri uri = Uri.withAppendedPath(
- ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number));
-
- Cursor cursor = null;
- String name = null;
- try {
- cursor = cr.query(uri,
- new String[] {ContactsContract.PhoneLookup.DISPLAY_NAME}, null, null, null);
- if (cursor != null && cursor.moveToFirst()) {
- name = cursor.getString(0);
- sContactNameCache.put(number, name);
- }
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
- return name;
- }
-
- private static String getDisplayName(
- Context context, String number, Uri gatewayOriginalAddress) {
- if (sDisplayNameCache == null) {
- sDisplayNameCache = new HashMap<>();
- } else {
- if (sDisplayNameCache.containsKey(number)) {
- return sDisplayNameCache.get(number);
- }
- }
-
- if (TextUtils.isEmpty(number)) {
- return context.getString(R.string.unknown);
- }
- ContentResolver cr = context.getContentResolver();
- String name;
- if (number.equals(getVoicemailNumber(context))) {
- name = context.getString(R.string.voicemail);
- } else {
- name = getContactNameFromNumber(cr, number);
- }
-
- if (name == null) {
- name = getFormattedNumber(context, number);
- }
- if (name == null && gatewayOriginalAddress != null) {
- name = gatewayOriginalAddress.getSchemeSpecificPart();
- }
- if (name == null) {
- name = context.getString(R.string.unknown);
- }
- sDisplayNameCache.put(number, name);
- return name;
- }
-
- public static String getVoicemailNumber(Context context) {
- if (sVoicemailNumber == null) {
- TelephonyManager tm =
- (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
- sVoicemailNumber = tm.getVoiceMailNumber();
- }
- return sVoicemailNumber;
- }
-
- public static String getFormattedNumber(Context context, @Nullable String number) {
- if (TextUtils.isEmpty(number)) {
- return "";
- }
-
- if (sFormattedNumberCache == null) {
- sFormattedNumberCache = new HashMap<>();
- } else {
- if (sFormattedNumberCache.containsKey(number)) {
- return sFormattedNumberCache.get(number);
- }
- }
-
- String countryIso = getSimRegionCode(context);
- String e164 = PhoneNumberUtils.formatNumberToE164(number, countryIso);
- String formattedNumber = PhoneNumberUtils.formatNumber(number, e164, countryIso);
- formattedNumber = TextUtils.isEmpty(formattedNumber) ? number : formattedNumber;
- sFormattedNumberCache.put(number, formattedNumber);
- return formattedNumber;
- }
-
- /**
- * Wrapper around TelephonyManager.getSimCountryIso() that will fallback to locale or USA ISOs
- * if it finds bogus data.
- */
- private static String getSimRegionCode(Context context) {
- TelephonyManager telephonyManager =
- (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
-
- // This can be null on some phones (and is null on robolectric default TelephonyManager)
- String countryIso = telephonyManager.getSimCountryIso();
- if (TextUtils.isEmpty(countryIso) || countryIso.length() != 2) {
- countryIso = Locale.getDefault().getCountry();
- if (countryIso == null || countryIso.length() != 2) {
- countryIso = "US";
- }
- }
-
- return countryIso.toUpperCase(Locale.US);
- }
-} \ No newline at end of file