diff options
41 files changed, 0 insertions, 3965 deletions
diff --git a/Android.mk b/Android.mk deleted file mode 100644 index 6900263..0000000 --- a/Android.mk +++ /dev/null @@ -1,69 +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_STATIC_JAVA_LIBRARIES += android-support-v4 -LOCAL_STATIC_JAVA_LIBRARIES += car-stream-lib - -LOCAL_AAPT_FLAGS += \ - --auto-add-overlay \ - -LOCAL_AAPT_FLAGS += --extra-packages com.android.car.radio.service -LOCAL_STATIC_JAVA_LIBRARIES += car-radio-service - -LOCAL_PACKAGE_NAME := Stream - -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_JAVA_LIBRARIES))) -LOCAL_RESOURCE_DIR += frameworks/support/v7/cardview/res -LOCAL_AAPT_FLAGS += --extra-packages android.support.v7.cardview -LOCAL_STATIC_JAVA_LIBRARIES += android-support-v7-cardview -endif - -# Include support-v7-palette, if not already included -ifeq (,$(findstring android-support-v7-palette,$(LOCAL_STATIC_JAVA_LIBRARIES))) -LOCAL_AAPT_FLAGS += --extra-packages android.support.v7.palette -LOCAL_STATIC_JAVA_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 • </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 |