Merge "Run getDisplayNameAndAvatarUri in background thread" into pi-car-dev
diff --git a/androidx-room/Android.mk b/androidx-room/Android.mk
index e9d215c..675dac8 100644
--- a/androidx-room/Android.mk
+++ b/androidx-room/Android.mk
@@ -36,6 +36,7 @@
car-apache-commons-codec-nodeps:$(COMMON_LIBS_PATH)/org/eclipse/tycho/tycho-bundles-external/0.18.1/eclipse/plugins/org.apache.commons.codec_1.4.0.v201209201156.jar \
car-auto-common-nodeps:$(COMMON_LIBS_PATH)/com/google/auto/auto-common/0.9/auto-common-0.9.jar \
car-javapoet-nodeps:$(COMMON_LIBS_PATH)/com/squareup/javapoet/1.8.0/javapoet-1.8.0.jar \
+ car-jetbrains-annotations-nodeps:$(COMMON_LIBS_PATH)/org/jetbrains/annotations/13.0/annotations-13.0.jar \
car-kotlin-metadata-nodeps:$(COMMON_LIBS_PATH)/me/eugeniomarletti/kotlin-metadata/1.2.1/kotlin-metadata-1.2.1.jar \
car-sqlite-jdbc-nodeps:$(COMMON_LIBS_PATH)/org/xerial/sqlite-jdbc/3.20.1/sqlite-jdbc-3.20.1.jar
diff --git a/car-apps-common/res/layout/control_bar_slot.xml b/car-apps-common/res/layout/control_bar_slot.xml
index bc09eed..41ef13e 100644
--- a/car-apps-common/res/layout/control_bar_slot.xml
+++ b/car-apps-common/res/layout/control_bar_slot.xml
@@ -15,8 +15,8 @@
-->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="@dimen/control_bar_button_slot_size"
- android:layout_height="@dimen/control_bar_button_slot_size"
+ android:layout_width="@dimen/control_bar_button_slot_width"
+ android:layout_height="@dimen/control_bar_button_slot_height"
android:layout_gravity="center_vertical"
android:visibility="visible"
android:foregroundGravity="center"
diff --git a/car-media-common/res/values-h668dp/dimens.xml b/car-apps-common/res/values-h600dp/dimens.xml
similarity index 72%
copy from car-media-common/res/values-h668dp/dimens.xml
copy to car-apps-common/res/values-h600dp/dimens.xml
index 3ca1445..dcbea47 100644
--- a/car-media-common/res/values-h668dp/dimens.xml
+++ b/car-apps-common/res/values-h600dp/dimens.xml
@@ -15,7 +15,8 @@
limitations under the License.
-->
<resources>
- <!-- App bar -->
- <!-- The height of app bar when it expends to 2 rows. Equals 2 * @dimen/appbar_first_row_height. -->
- <dimen name="appbar_2_rows_height">192dp</dimen>
+ <dimen name="control_bar_button_background_radius">48dp</dimen>
+ <dimen name="control_bar_button_size">96dp</dimen>
+ <dimen name="control_bar_button_padding">26dp</dimen>
+ <dimen name="minimized_control_bar_button_size">96dp</dimen>
</resources>
diff --git a/car-apps-common/res/values/dimens.xml b/car-apps-common/res/values/dimens.xml
index b2993e3..1a17bd3 100644
--- a/car-apps-common/res/values/dimens.xml
+++ b/car-apps-common/res/values/dimens.xml
@@ -27,10 +27,11 @@
<dimen name="control_bar_margin_x">@*android:dimen/car_margin</dimen>
<dimen name="control_bar_margin_bottom">@*android:dimen/car_padding_2</dimen>
<dimen name="control_bar_button_size">76dp</dimen>
- <dimen name="control_bar_button_slot_size">@dimen/control_bar_height</dimen>
+ <dimen name="control_bar_button_slot_height">@dimen/control_bar_height</dimen>
+ <dimen name="control_bar_button_slot_width">@dimen/control_bar_button_size</dimen>
<dimen name="control_bar_elevation">0dp</dimen>
<dimen name="control_bar_button_padding">16dp</dimen>
- <dimen name="control_bar_button_background_radius">48dp</dimen>
+ <dimen name="control_bar_button_background_radius">38dp</dimen>
<!-- Overflow button in control Bar -->
<dimen name="overflow_button_icon_size">44dp</dimen>
diff --git a/car-assist-client-lib/Android.mk b/car-assist-client-lib/Android.mk
index 82dc48d..ae687ac 100644
--- a/car-assist-client-lib/Android.mk
+++ b/car-assist-client-lib/Android.mk
@@ -39,3 +39,7 @@
car-assist-lib \
include $(BUILD_STATIC_JAVA_LIBRARY)
+
+ifeq (,$(ONE_SHOT_MAKEFILE))
+ include $(call all-makefiles-under,$(LOCAL_PATH))
+endif
diff --git a/car-assist-client-lib/tests/Android.mk b/car-assist-client-lib/tests/Android.mk
new file mode 100644
index 0000000..9f0a4e8
--- /dev/null
+++ b/car-assist-client-lib/tests/Android.mk
@@ -0,0 +1,19 @@
+# Copyright (C) 2019 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.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+# Include all makefiles in subdirectories
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/car-assist-client-lib/tests/robotests/Android.mk b/car-assist-client-lib/tests/robotests/Android.mk
new file mode 100644
index 0000000..93613b6
--- /dev/null
+++ b/car-assist-client-lib/tests/robotests/Android.mk
@@ -0,0 +1,78 @@
+LOCAL_PATH := $(call my-dir)
+
+############################################################
+# CarAssistClient app just for Robolectric test target. #
+############################################################
+include $(CLEAR_VARS)
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_PACKAGE_NAME := CarAssistClient
+LOCAL_PRIVATE_PLATFORM_APIS := true
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_USE_AAPT2 := true
+
+LOCAL_PRIVILEGED_MODULE := true
+
+LOCAL_JAVA_LIBRARIES := android.car
+
+LOCAL_STATIC_ANDROID_LIBRARIES := \
+ car-assist-client-lib
+
+include $(BUILD_PACKAGE)
+
+#############################################
+# Car-Assist-Client Robolectric test target. #
+#############################################
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := CarAssistClientRoboTests
+LOCAL_MODULE_CLASS := JAVA_LIBRARIES
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_JAVA_RESOURCE_DIRS := config
+
+# Include the testing libraries
+LOCAL_JAVA_LIBRARIES := \
+ android.car \
+ robolectric_android-all-stub \
+ Robolectric_all-target \
+ mockito-robolectric-prebuilt \
+ truth-prebuilt
+
+LOCAL_INSTRUMENTATION_FOR := CarAssistClient
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+#############################################################
+# CarAssistClient runner target to run the previous target. #
+#############################################################
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := RunCarAssistClientRoboTests
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ CarAssistClientRoboTests
+
+LOCAL_JAVA_LIBRARIES := \
+ CarAssistClientRoboTests \
+ robolectric_android-all-stub \
+ Robolectric_all-target \
+ mockito-robolectric-prebuilt \
+ truth-prebuilt \
+ android.car
+
+LOCAL_TEST_PACKAGE := CarAssistClient
+
+LOCAL_ROBOTEST_FILES := \
+ $(call find-files-in-subdirs,$(LOCAL_PATH)/src,*Test.java,.))
+
+LOCAL_INSTRUMENT_SOURCE_DIRS := $(dir $(LOCAL_PATH))../src
+
+include external/robolectric-shadows/run_robotests.mk
diff --git a/car-assist-client-lib/tests/robotests/AndroidManifest.xml b/car-assist-client-lib/tests/robotests/AndroidManifest.xml
new file mode 100644
index 0000000..f744c6c
--- /dev/null
+++ b/car-assist-client-lib/tests/robotests/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2019 Google Inc.
+
+ 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.assist.client.robotests">
+
+</manifest>
diff --git a/car-assist-client-lib/tests/robotests/config/robolectric_properties b/car-assist-client-lib/tests/robotests/config/robolectric_properties
new file mode 100644
index 0000000..12ca377
--- /dev/null
+++ b/car-assist-client-lib/tests/robotests/config/robolectric_properties
@@ -0,0 +1,17 @@
+#
+# Copyright (C) 2019 Google Inc.
+#
+# 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=packages/apps/Car/libs/car-assist-client-lib/tests/robotests/AndroidManifest.xml
+sdk=NEWEST_SDK
diff --git a/car-assist-client-lib/tests/robotests/src/com/android/car/assist/client/CarAssistUtilsTest.java b/car-assist-client-lib/tests/robotests/src/com/android/car/assist/client/CarAssistUtilsTest.java
new file mode 100644
index 0000000..8e9770f
--- /dev/null
+++ b/car-assist-client-lib/tests/robotests/src/com/android/car/assist/client/CarAssistUtilsTest.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2019 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.assist.client;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+
+import androidx.core.app.NotificationCompat;
+import androidx.core.app.NotificationCompat.Action;
+import androidx.core.app.NotificationCompat.MessagingStyle;
+import androidx.core.app.Person;
+import androidx.core.app.RemoteInput;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class CarAssistUtilsTest {
+
+ private Context mContext;
+ private static final String PKG_1 = "package_1";
+ private static final String OP_PKG = "OpPackage";
+ private static final int ID = 1;
+ private static final String TAG = "Tag";
+ private static final int UID = 2;
+ private static final int INITIAL_PID = 3;
+ private static final String CHANNEL_ID = "CHANNEL_ID";
+ private static final String CONTENT_TITLE = "CONTENT_TITLE";
+ private static final String STATIC_USER_NAME = "STATIC_USER_NAME";
+ private static final String SENDER_NAME = "Larry";
+ private static final String SENDER_CONTACT_URI = "TEST_SENDER_URI";
+ private static final String REMOTE_INPUT_KEY = "REMOTE_INPUT_KEY";
+ private static final String REPLY_ACTION = "test.package.REPLY";
+ private static final String READ_ACTION = "test.package.READ";
+ private static final long POST_TIME = 12345L;
+ private static final int ICON = android.R.drawable.ic_media_play;
+ private static final String OVERRIDE_GROUP_KEY = "OVERRIDE_GROUP_KEY";
+ private static final UserHandle USER_HANDLE = new UserHandle(12);
+
+ @Before
+ public void setup() {
+ mContext = RuntimeEnvironment.application;
+ }
+
+ @Test
+ public void testCarCompatMessagingNotification_qualifyingNotification() {
+ assertThat(CarAssistUtils.isCarCompatibleMessagingNotification(
+ buildStatusBarNotification(/* hasReplyAction */ true, /* hasMessagingStyle */
+ true))).isTrue();
+ }
+
+ @Test
+ public void testCarCompatMessagingNotification_noReplyNotification() {
+ assertThat(CarAssistUtils.isCarCompatibleMessagingNotification(
+ buildStatusBarNotification(/* hasReplyAction */ false, /* hasMessagingStyle */
+ true))).isTrue();
+ }
+
+ @Test
+ public void testCarCompatMessagingNotifcation_noMessagingStyleNotification() {
+ assertThat(CarAssistUtils.isCarCompatibleMessagingNotification(
+ buildStatusBarNotification(/* hasReplyAction */ true, /* hasMessagingStyle */
+ false))).isFalse();
+ }
+
+ private StatusBarNotification buildStatusBarNotification(boolean hasReplyAction,
+ boolean hasMessagingStyle) {
+
+ NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext, CHANNEL_ID)
+ .setContentTitle(CONTENT_TITLE)
+ .setCategory(Notification.CATEGORY_MESSAGE)
+ .addAction(buildMarkAsReadAction())
+ .setShowWhen(true);
+
+ if (hasReplyAction) {
+ builder.addAction(buildReplyAction());
+ }
+
+ if (hasMessagingStyle) {
+ builder.setStyle(buildMessagingStyle());
+ }
+
+ return new StatusBarNotification(PKG_1, OP_PKG,
+ ID, TAG, UID, INITIAL_PID, builder.build(), USER_HANDLE,
+ OVERRIDE_GROUP_KEY, POST_TIME);
+ }
+
+ private Action buildMarkAsReadAction() {
+ Intent intent = new Intent(mContext, this.getClass()).setAction(READ_ACTION);
+ PendingIntent pendingIntent = PendingIntent.getService(mContext, 0, intent,
+ PendingIntent.FLAG_UPDATE_CURRENT);
+ return new Action.Builder(ICON, "read", pendingIntent)
+ .setSemanticAction(Action.SEMANTIC_ACTION_MARK_AS_READ)
+ .setShowsUserInterface(false)
+ .build();
+ }
+
+ private Action buildReplyAction() {
+ Intent intent = new Intent(mContext, this.getClass())
+ .setAction(REPLY_ACTION);
+ PendingIntent replyIntent = PendingIntent.getService(mContext, 0, intent,
+ PendingIntent.FLAG_UPDATE_CURRENT);
+
+ return new Action.Builder(ICON,
+ "reply", replyIntent)
+ .setSemanticAction(Action.SEMANTIC_ACTION_REPLY)
+ .setShowsUserInterface(false)
+ .addRemoteInput(
+ new RemoteInput.Builder(REMOTE_INPUT_KEY)
+ .build()
+ )
+ .build();
+ }
+
+ private MessagingStyle buildMessagingStyle() {
+ Person user = new Person.Builder()
+ .setName(STATIC_USER_NAME)
+ .build();
+ NotificationCompat.MessagingStyle messagingStyle =
+ new NotificationCompat.MessagingStyle(user);
+ Person sender = new Person.Builder()
+ .setName(SENDER_NAME)
+ .setUri(SENDER_CONTACT_URI)
+ .build();
+ messagingStyle.addMessage("Hello World", POST_TIME, sender);
+ return messagingStyle;
+ }
+}
diff --git a/car-assist-client-lib/tests/robotests/src/com/android/car/assist/client/TestConfig.java b/car-assist-client-lib/tests/robotests/src/com/android/car/assist/client/TestConfig.java
new file mode 100644
index 0000000..6c2a92b
--- /dev/null
+++ b/car-assist-client-lib/tests/robotests/src/com/android/car/assist/client/TestConfig.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2019 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.assist.client;
+
+public class TestConfig {
+ public static final int SDK_VERSION = 28;
+ public static final String MANIFEST_PATH =
+ "packages/apps/Car/libs/car-assist-client-lib/AndroidManifest.xml";
+}
diff --git a/car-media-common/res/values-h668dp/dimens.xml b/car-media-common/res/values-h600dp/dimens.xml
similarity index 79%
rename from car-media-common/res/values-h668dp/dimens.xml
rename to car-media-common/res/values-h600dp/dimens.xml
index 3ca1445..b1bcd84 100644
--- a/car-media-common/res/values-h668dp/dimens.xml
+++ b/car-media-common/res/values-h600dp/dimens.xml
@@ -15,7 +15,8 @@
limitations under the License.
-->
<resources>
- <!-- App bar -->
- <!-- The height of app bar when it expends to 2 rows. Equals 2 * @dimen/appbar_first_row_height. -->
<dimen name="appbar_2_rows_height">192dp</dimen>
+ <dimen name="appbar_view_icon_touch_target_size">96dp</dimen>
+ <dimen name="appbar_view_icon_padding">26dp</dimen>
+ <dimen name="appbar_view_icon_background_radius">48dp</dimen>
</resources>
diff --git a/car-media-common/res/values/bools.xml b/car-media-common/res/values/bools.xml
index e6b0d91..ffe316b 100644
--- a/car-media-common/res/values/bools.xml
+++ b/car-media-common/res/values/bools.xml
@@ -24,9 +24,9 @@
-->
<bool name="flag_invalid_media_art">false</bool>
- <!-- Whether to show a linear progress bar in control bar and minimized control bar or not. -->
- <bool name="show_linear_progress_bar">false</bool>
+ <!-- Whether to show a linear progress bar in minimized control bar or not. -->
+ <bool name="show_linear_progress_bar">true</bool>
<!-- Whether to show a circular progress bar in control bar and minimized control bar or not. -->
- <bool name="show_circular_progress_bar">true</bool>
+ <bool name="show_circular_progress_bar">false</bool>
</resources>
diff --git a/car-media-common/res/values/dimens.xml b/car-media-common/res/values/dimens.xml
index c5ea201..8bf9a60 100644
--- a/car-media-common/res/values/dimens.xml
+++ b/car-media-common/res/values/dimens.xml
@@ -27,10 +27,10 @@
<dimen name="appbar_second_row_height">0dp</dimen>
<!-- The height of app bar when it expends to 2 rows. Equals 2 * @dimen/appbar_first_row_height. -->
<dimen name="appbar_2_rows_height">160dp</dimen>
- <dimen name="appbar_view_icon_touch_target_size">96dp</dimen>
+ <dimen name="appbar_view_icon_touch_target_size">76dp</dimen>
<dimen name="appbar_view_icon_size">@*android:dimen/car_primary_icon_size</dimen>
<!-- Padding of primary icon, equals (@dimen/appbar_view_icon_touch_target_size - @dimen/appbar_view_icon_size) / 2. -->
- <dimen name="appbar_view_icon_padding">26dp</dimen>
+ <dimen name="appbar_view_icon_padding">16dp</dimen>
<!-- playback_fragment.xml -->
<dimen name="playback_fragment_text_margin_top">@*android:dimen/car_padding_4</dimen>
@@ -61,5 +61,5 @@
<dimen name="minimized_progress_bar_track_thickness">4dp</dimen>
<!-- appbar_view_icon_background.xml -->
- <dimen name="appbar_view_icon_background_radius">48dp</dimen>
+ <dimen name="appbar_view_icon_background_radius">38dp</dimen>
</resources>
diff --git a/car-media-common/src/com/android/car/media/common/source/MediaSource.java b/car-media-common/src/com/android/car/media/common/source/MediaSource.java
index ed4e9ae..34516bd 100644
--- a/car-media-common/src/com/android/car/media/common/source/MediaSource.java
+++ b/car-media-common/src/com/android/car/media/common/source/MediaSource.java
@@ -44,7 +44,7 @@
/**
* This represents a source of media content. It provides convenient methods to access media source
- * metadata, such as primary color and application name.
+ * metadata, such as application name and icon.
*/
public class MediaSource {
private static final String TAG = "MediaSource";
@@ -59,14 +59,11 @@
}
@NonNull
- private final String mPackageName;
- @Nullable
- private String mBrowseServiceClassName;
+ private final ComponentName mBrowseService;
@NonNull
- private final Context mContext;
- @Nullable
- private final ServiceInfo mServiceInfo;
- private CharSequence mName;
+ private final CharSequence mDisplayName;
+ @NonNull
+ private final Drawable mIcon;
/**
* Creates a {@link MediaSource} for the given {@link ComponentName}
@@ -74,19 +71,32 @@
@Nullable
public static MediaSource create(@NonNull Context context,
@NonNull ComponentName componentName) {
- MediaSource mediaSource = new MediaSource(context, componentName);
- if (mediaSource.mBrowseServiceClassName == null) {
+ ServiceInfo serviceInfo = getBrowseServiceInfo(context, componentName);
+
+ String className = serviceInfo != null ? serviceInfo.name : null;
+ if (TextUtils.isEmpty(className)) {
+ Log.w(TAG,
+ "No MediaBrowserService found in component " + componentName.flattenToString());
return null;
}
- return mediaSource;
+
+ try {
+ String packageName = componentName.getPackageName();
+ CharSequence displayName = extractDisplayName(context, serviceInfo, packageName);
+ Drawable icon = extractIcon(context, serviceInfo, packageName);
+ ComponentName browseService = new ComponentName(packageName, className);
+ return new MediaSource(browseService, displayName, icon);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(TAG, "Component not found " + componentName.flattenToString());
+ return null;
+ }
}
- // TODO(b/136274456): Clean up the internals of MediaSource.
- private MediaSource(@NonNull Context context, @NonNull ComponentName componentName) {
- mContext = context;
- mPackageName = componentName.getPackageName();
- mServiceInfo = getBrowseServiceInfo(componentName);
- extractComponentInfo();
+ private MediaSource(@NonNull ComponentName browseService, @NonNull CharSequence displayName,
+ @NonNull Drawable icon) {
+ mBrowseService = browseService;
+ mDisplayName = displayName;
+ mIcon = icon;
}
/**
@@ -96,8 +106,9 @@
* connect and handle rejections gracefully.
*/
@Nullable
- private ServiceInfo getBrowseServiceInfo(@NonNull ComponentName componentName) {
- PackageManager packageManager = mContext.getPackageManager();
+ private static ServiceInfo getBrowseServiceInfo(@NonNull Context context,
+ @NonNull ComponentName componentName) {
+ PackageManager packageManager = context.getPackageManager();
Intent intent = new Intent();
intent.setAction(MediaBrowserService.SERVICE_INTERFACE);
intent.setPackage(componentName.getPackageName());
@@ -119,99 +130,82 @@
return null;
}
- private void extractComponentInfo() {
- mBrowseServiceClassName = mServiceInfo != null ? mServiceInfo.name : null;
- try {
- // Gets a proper app name. Checks service label first. If failed, uses application
- // label as fallback.
- if (mServiceInfo != null && mServiceInfo.labelRes != 0) {
- mName = mServiceInfo.loadLabel(mContext.getPackageManager());
- } else {
- ApplicationInfo applicationInfo =
- mContext.getPackageManager().getApplicationInfo(mPackageName,
- PackageManager.GET_META_DATA);
- mName = applicationInfo.loadLabel(mContext.getPackageManager());
- }
- } catch (PackageManager.NameNotFoundException e) {
- Log.w(TAG, "Unable to update media client package attributes.", e);
+ /**
+ * @return a proper app name. Checks service label first. If failed, uses application label
+ * as fallback.
+ */
+ @NonNull
+ private static CharSequence extractDisplayName(@NonNull Context context,
+ @Nullable ServiceInfo serviceInfo, @NonNull String packageName)
+ throws PackageManager.NameNotFoundException {
+ if (serviceInfo != null && serviceInfo.labelRes != 0) {
+ return serviceInfo.loadLabel(context.getPackageManager());
}
+ ApplicationInfo applicationInfo =
+ context.getPackageManager().getApplicationInfo(packageName,
+ PackageManager.GET_META_DATA);
+ return applicationInfo.loadLabel(context.getPackageManager());
+ }
+
+ /**
+ * @return a proper icon. Checks service icon first. If failed, uses application icon as
+ * fallback.
+ */
+ @NonNull
+ private static Drawable extractIcon(@NonNull Context context, @Nullable ServiceInfo serviceInfo,
+ @NonNull String packageName) throws PackageManager.NameNotFoundException {
+ return serviceInfo != null ? serviceInfo.loadIcon(context.getPackageManager())
+ : context.getPackageManager().getApplicationIcon(packageName);
}
/**
* @return media source human readable name for display.
*/
+ @NonNull
public CharSequence getDisplayName() {
- return mName;
+ return mDisplayName;
}
/**
* @return the package name of this media source.
*/
+ @NonNull
public String getPackageName() {
- return mPackageName;
+ return mBrowseService.getPackageName();
}
/**
* @return a {@link ComponentName} referencing this media source's {@link MediaBrowserService},
* or NULL if this media source doesn't implement such service.
*/
- @Nullable
+ @NonNull
public ComponentName getBrowseServiceComponentName() {
- if (mBrowseServiceClassName != null) {
- return new ComponentName(mPackageName, mBrowseServiceClassName);
- } else {
- return null;
- }
+ return mBrowseService;
}
/**
* @return a {@link Drawable} as the media source's icon.
*/
+ @NonNull
public Drawable getIcon() {
- // Checks service icon first. If failed, uses application icon as fallback.
- try {
- if (mServiceInfo != null) {
- return mServiceInfo.loadIcon(mContext.getPackageManager());
- }
- return mContext.getPackageManager().getApplicationIcon(getPackageName());
- } catch (PackageManager.NameNotFoundException e) {
- return null;
- }
+ return mIcon;
}
/**
* Returns this media source's icon cropped to a circle.
*/
public Bitmap getRoundPackageIcon() {
- Drawable icon = getIcon();
- if (icon != null) {
- return getRoundCroppedBitmap(drawableToBitmap(icon));
- }
- return null;
+ return getRoundCroppedBitmap(drawableToBitmap(mIcon));
}
/**
* Returns {@code true} iff this media source should not be templatized.
*/
public boolean isCustom() {
- return isCustom(mPackageName);
+ return CUSTOM_MEDIA_SOURCES.contains(getPackageName());
}
- /**
- * Returns {@code true} iff the provided media package should not be templatized.
- */
- public static boolean isCustom(String packageName) {
- return CUSTOM_MEDIA_SOURCES.contains(packageName);
- }
-
- /**
- * Returns {@code true} iff this media source has a browse service to connect to.
- */
- public boolean isBrowsable() {
- return mBrowseServiceClassName != null;
- }
-
- private Bitmap drawableToBitmap(Drawable drawable) {
+ private static Bitmap drawableToBitmap(Drawable drawable) {
Bitmap bitmap = null;
if (drawable instanceof BitmapDrawable) {
@@ -234,7 +228,7 @@
return bitmap;
}
- private Bitmap getRoundCroppedBitmap(Bitmap bitmap) {
+ private static Bitmap getRoundCroppedBitmap(Bitmap bitmap) {
Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(),
Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(output);
@@ -258,24 +252,17 @@
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MediaSource that = (MediaSource) o;
- return Objects.equals(mPackageName, that.mPackageName)
- && Objects.equals(mBrowseServiceClassName, that.mBrowseServiceClassName);
+ return Objects.equals(mBrowseService, that.mBrowseService);
}
@Override
public int hashCode() {
- return Objects.hash(mPackageName, mBrowseServiceClassName);
+ return Objects.hash(mBrowseService);
}
@Override
@NonNull
public String toString() {
- return mPackageName + (mBrowseServiceClassName == null ? "" : mBrowseServiceClassName);
- }
-
- /** Returns the package name of the given source, or null. */
- @Nullable
- public static String getPackageName(@Nullable MediaSource source) {
- return (source != null) ? source.getPackageName() : null;
+ return mBrowseService.flattenToString();
}
}
diff --git a/car-media-common/src/com/android/car/media/common/source/MediaSourceViewModel.java b/car-media-common/src/com/android/car/media/common/source/MediaSourceViewModel.java
index 17479dc..d25ebe9 100644
--- a/car-media-common/src/com/android/car/media/common/source/MediaSourceViewModel.java
+++ b/car-media-common/src/com/android/car/media/common/source/MediaSourceViewModel.java
@@ -231,9 +231,6 @@
}
ComponentName browseService = newMediaSource.getBrowseServiceComponentName();
- if (browseService == null) {
- Log.e(TAG, "No browseService for source: " + newMediaSource.getPackageName());
- }
mBrowserConnector.connectTo(browseService);
}
}
diff --git a/car-media-common/src/com/android/car/media/common/source/MediaSourcesLiveData.java b/car-media-common/src/com/android/car/media/common/source/MediaSourcesLiveData.java
index 475fd81..6abdab2 100644
--- a/car-media-common/src/com/android/car/media/common/source/MediaSourcesLiveData.java
+++ b/car-media-common/src/com/android/car/media/common/source/MediaSourcesLiveData.java
@@ -97,11 +97,6 @@
Log.w(TAG, "Media source is null");
return false;
}
- if (mediaSource.getDisplayName() == null) {
- Log.w(TAG,
- "Found media source without name: " + mediaSource.toString());
- return false;
- }
return true;
})
.sorted(Comparator.comparing(
@@ -112,37 +107,20 @@
}
/**
- * Generates a set of all possible activities and media services to choose from.
+ * Generates a set of all possible media services to choose from.
*/
private Set<ComponentName> getComponentNames() {
PackageManager packageManager = mAppContext.getPackageManager();
- Intent intent = new Intent(Intent.ACTION_MAIN, null);
- intent.addCategory(Intent.CATEGORY_APP_MUSIC);
-
Intent mediaIntent = new Intent();
mediaIntent.setAction(MediaBrowserService.SERVICE_INTERFACE);
-
- List<ResolveInfo> availableActivities = packageManager.queryIntentActivities(intent, 0);
List<ResolveInfo> mediaServices = packageManager.queryIntentServices(mediaIntent,
PackageManager.GET_RESOLVED_FILTER);
Set<ComponentName> components = new HashSet<>();
- Set<String> packages = new HashSet<>();
for (ResolveInfo info : mediaServices) {
ComponentName componentName = new ComponentName(info.serviceInfo.packageName,
info.serviceInfo.name);
components.add(componentName);
- packages.add(info.serviceInfo.packageName);
- }
- for (ResolveInfo info : availableActivities) {
- // If a service has been added to the map, don't add the activity belonging to the
- // same package.
- if (packages.contains(info.activityInfo.packageName)) {
- continue;
- }
- ComponentName componentName = new ComponentName(info.activityInfo.packageName,
- info.activityInfo.name);
- components.add(componentName);
}
return components;
}
diff --git a/car-telephony-common/src/com/android/car/telephony/common/Contact.java b/car-telephony-common/src/com/android/car/telephony/common/Contact.java
index e95cb3e..43e2cdc 100644
--- a/car-telephony-common/src/com/android/car/telephony/common/Contact.java
+++ b/car-telephony-common/src/com/android/car/telephony/common/Contact.java
@@ -38,6 +38,8 @@
*/
public class Contact implements Parcelable, Comparable<Contact> {
private static final String TAG = "CD.Contact";
+ private static final String PHONEBOOK_LABEL = "phonebook_label";
+ private static final String PHONEBOOK_LABEL_ALT = "phonebook_label_alt";
/**
* Contact belongs to TYPE_LETTER if its display name starts with a letter
@@ -102,6 +104,25 @@
private String mAltDisplayName;
/**
+ * The phonebook label.
+ * <p>
+ * For {@link #mDisplayName}s starting with letters, label will be the first character of
+ * {@link #mDisplayName}. For {@link #mDisplayName}s starting with numbers, the label will
+ * be "#". For {@link #mDisplayName}s starting with other characters, the label will be "...".
+ * </p>
+ */
+ private String mPhoneBookLabel;
+
+ /**
+ * The alternative phonebook label.
+ * <p>
+ * It is similar with {@link #mPhoneBookLabel}. But instead of generating from
+ * {@link #mDisplayName}, it will use {@link #mAltDisplayName}.
+ * </p>
+ */
+ private String mPhoneBookLabelAlt;
+
+ /**
* A URI that can be used to retrieve a thumbnail of the contact's photo.
*/
private Uri mAvatarThumbnailUri;
@@ -138,6 +159,8 @@
ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME);
int altDisplayNameColumn = cursor.getColumnIndex(
ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME_ALTERNATIVE);
+ int phoneBookLabelColumn = cursor.getColumnIndex(PHONEBOOK_LABEL);
+ int phoneBookLabelAltColumn = cursor.getColumnIndex(PHONEBOOK_LABEL_ALT);
int avatarUriColumn = cursor.getColumnIndex(
ContactsContract.CommonDataKinds.Phone.PHOTO_URI);
int avatarThumbnailColumn = cursor.getColumnIndex(
@@ -149,6 +172,8 @@
contact.mId = cursor.getLong(contactIdColumn);
contact.mDisplayName = cursor.getString(displayNameColumn);
contact.mAltDisplayName = cursor.getString(altDisplayNameColumn);
+ contact.mPhoneBookLabel = cursor.getString(phoneBookLabelColumn);
+ contact.mPhoneBookLabelAlt = cursor.getString(phoneBookLabelAltColumn);
PhoneNumber number = PhoneNumber.fromCursor(context, cursor);
contact.mPhoneNumbers.add(number);
@@ -203,6 +228,20 @@
return mAltDisplayName;
}
+ /**
+ * Returns {@link #mPhoneBookLabel}
+ */
+ public String getPhonebookLabel() {
+ return mPhoneBookLabel;
+ }
+
+ /**
+ * Returns {@link #mPhoneBookLabelAlt}
+ */
+ public String getPhonebookLabelAlt() {
+ return mPhoneBookLabelAlt;
+ }
+
public boolean isVoicemail() {
return mIsVoiceMail;
}
@@ -300,6 +339,8 @@
}
dest.writeString(mDisplayName);
dest.writeString(mAltDisplayName);
+ dest.writeString(mPhoneBookLabel);
+ dest.writeString(mPhoneBookLabelAlt);
dest.writeParcelable(mAvatarThumbnailUri, 0);
dest.writeParcelable(mAvatarUri, 0);
dest.writeString(mLookupKey);
@@ -335,6 +376,8 @@
}
contact.mDisplayName = source.readString();
contact.mAltDisplayName = source.readString();
+ contact.mPhoneBookLabel = source.readString();
+ contact.mPhoneBookLabelAlt = source.readString();
contact.mAvatarThumbnailUri = source.readParcelable(Uri.class.getClassLoader());
contact.mAvatarUri = source.readParcelable(Uri.class.getClassLoader());
contact.mLookupKey = source.readString();
@@ -354,7 +397,8 @@
* letters, numbers, then special characters.
*/
public int compareByDisplayName(@NonNull Contact otherContact) {
- return compareNames(mDisplayName, otherContact.getDisplayName());
+ return compareNames(mDisplayName, otherContact.getDisplayName(),
+ mPhoneBookLabel, otherContact.getPhonebookLabel());
}
/**
@@ -362,15 +406,16 @@
* letters, numbers, then special characters.
*/
public int compareByAltDisplayName(@NonNull Contact otherContact) {
- return compareNames(mAltDisplayName, otherContact.getAltDisplayName());
+ return compareNames(mAltDisplayName, otherContact.getAltDisplayName(),
+ mPhoneBookLabelAlt, otherContact.getPhonebookLabelAlt());
}
/**
* Compares two strings in an order of letters, numbers, then special characters.
*/
- private int compareNames(String name, String otherName) {
- int type = getNameType(name);
- int otherType = getNameType(otherName);
+ private int compareNames(String name, String otherName, String label, String otherLabel) {
+ int type = getNameType(label);
+ int otherType = getNameType(otherLabel);
if (type != otherType) {
return Integer.compare(type, otherType);
}
@@ -378,13 +423,17 @@
return collator.compare(name == null ? "" : name, otherName == null ? "" : otherName);
}
- private static int getNameType(String displayName) {
+ /**
+ * Returns the type of the name string.
+ * Types can be {@link #TYPE_LETTER}, {@link #TYPE_DIGIT} and {@link #TYPE_OTHER}.
+ */
+ private static int getNameType(String label) {
// A helper function to classify Contacts
- if (!TextUtils.isEmpty(displayName)) {
- if (Character.isLetter(displayName.charAt(0))) {
+ if (!TextUtils.isEmpty(label)) {
+ if (Character.isLetter(label.charAt(0))) {
return TYPE_LETTER;
}
- if (Character.isDigit(displayName.charAt(0))) {
+ if (label.contains("#")) {
return TYPE_DIGIT;
}
}
diff --git a/car-telephony-common/src/com/android/car/telephony/common/InMemoryPhoneBook.java b/car-telephony-common/src/com/android/car/telephony/common/InMemoryPhoneBook.java
index 24529b8..73a31c9 100644
--- a/car-telephony-common/src/com/android/car/telephony/common/InMemoryPhoneBook.java
+++ b/car-telephony-common/src/com/android/car/telephony/common/InMemoryPhoneBook.java
@@ -88,11 +88,14 @@
private InMemoryPhoneBook(Context context) {
mContext = context;
+ // TODO(b/138749585): clean up filtering once contact cloud sync is disabled.
QueryParam contactListQueryParam = new QueryParam(
ContactsContract.Data.CONTENT_URI,
null,
- ContactsContract.Data.MIMETYPE + " = ?",
- new String[]{ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE},
+ ContactsContract.Data.MIMETYPE + " = ? and "
+ + ContactsContract.RawContacts.ACCOUNT_TYPE + " != ?",
+ new String[]{
+ ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE, "com.google"},
ContactsContract.Contacts.DISPLAY_NAME + " ASC ");
mContactListAsyncQueryLiveData = new AsyncQueryLiveData<List<Contact>>(mContext,
QueryParam.of(contactListQueryParam)) {
diff --git a/car-telephony-common/src/com/android/car/telephony/common/PhoneNumber.java b/car-telephony-common/src/com/android/car/telephony/common/PhoneNumber.java
index 85893e0..5f38799 100644
--- a/car-telephony-common/src/com/android/car/telephony/common/PhoneNumber.java
+++ b/car-telephony-common/src/com/android/car/telephony/common/PhoneNumber.java
@@ -23,7 +23,9 @@
import android.os.Parcelable;
import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.text.TextUtils;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.Objects;
@@ -34,15 +36,17 @@
public class PhoneNumber implements Parcelable {
private final I18nPhoneNumberWrapper mI18nPhoneNumber;
- private final int mType;
- @Nullable
- private final String mLabel;
+ @NonNull
+ private final String mAccountName;
+ @NonNull
+ private final String mAccountType;
+ private int mType;
+ @Nullable
+ private String mLabel;
private boolean mIsPrimary;
private long mId;
private int mDataVersion;
- private String mAccountName;
- private String mAccountType;
static PhoneNumber fromCursor(Context context, Cursor cursor) {
int typeColumn = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.TYPE);
@@ -99,22 +103,22 @@
mLabel = label;
mIsPrimary = isPrimary;
mId = id;
- mAccountName = accountName;
- mAccountType = accountType;
+ mAccountName = TextUtils.emptyIfNull(accountName);
+ mAccountType = TextUtils.emptyIfNull(accountType);
mDataVersion = dataVersion;
}
@Override
public boolean equals(Object obj) {
return obj instanceof PhoneNumber
- && ((PhoneNumber) obj).mType == mType
- && Objects.equals(((PhoneNumber) obj).mLabel, mLabel)
- && mI18nPhoneNumber.equals(((PhoneNumber) obj).mI18nPhoneNumber);
+ && mI18nPhoneNumber.equals(((PhoneNumber) obj).mI18nPhoneNumber)
+ && mAccountName.equals(((PhoneNumber) obj).mAccountName)
+ && mAccountType.equals(((PhoneNumber) obj).mAccountType);
}
@Override
public int hashCode() {
- return Objects.hash(mI18nPhoneNumber, mType, mLabel);
+ return Objects.hash(mI18nPhoneNumber, mAccountName, mAccountType);
}
/**
@@ -188,8 +192,8 @@
mDataVersion = phoneNumber.mDataVersion;
mId = phoneNumber.mId;
mIsPrimary |= phoneNumber.mIsPrimary;
- mAccountName = phoneNumber.mAccountName;
- mAccountType = phoneNumber.mAccountType;
+ mType = phoneNumber.mType;
+ mLabel = phoneNumber.mLabel;
}
}
return this;
@@ -205,7 +209,7 @@
@Override
public String toString() {
- return getNumber() + " " + String.valueOf(mLabel);
+ return getNumber() + " " + mAccountName + " " + mAccountType;
}
@Override
diff --git a/car-telephony-common/src/com/android/car/telephony/common/TelecomUtils.java b/car-telephony-common/src/com/android/car/telephony/common/TelecomUtils.java
index a517c89..7cb60ae 100644
--- a/car-telephony-common/src/com/android/car/telephony/common/TelecomUtils.java
+++ b/car-telephony-common/src/com/android/car/telephony/common/TelecomUtils.java
@@ -338,17 +338,10 @@
final String displayName) {
LetterTileDrawable letterTileDrawable = createLetterTile(context, displayName);
- if (avatarUri != null) {
- Glide.with(context)
- .load(avatarUri)
- .apply(new RequestOptions().centerCrop().error(letterTileDrawable))
- .into(icon);
- return;
- }
-
- // Use the letter tile as avatar if there is no avatar available from content provider.
- icon.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
- icon.setImageDrawable(letterTileDrawable);
+ Glide.with(context)
+ .load(avatarUri)
+ .apply(new RequestOptions().centerCrop().error(letterTileDrawable))
+ .into(icon);
}
/** Create a {@link LetterTileDrawable} for the given display name. */