diff options
9 files changed, 472 insertions, 17 deletions
diff --git a/packages/CompanionDeviceManager/res/drawable/helper_ok_button.xml b/packages/CompanionDeviceManager/res/drawable/helper_ok_button.xml new file mode 100644 index 000000000000..f9ec5d0dce55 --- /dev/null +++ b/packages/CompanionDeviceManager/res/drawable/helper_ok_button.xml @@ -0,0 +1,22 @@ +<!-- + ~ Copyright (C) 2022 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. + --> + +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <solid android:color="@android:color/system_accent1_100"/> + <corners android:topLeftRadius="16dp" android:topRightRadius="16dp" + android:bottomLeftRadius="16dp" android:bottomRightRadius="16dp"/> +</shape>
\ No newline at end of file diff --git a/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml b/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml index 70cbfdf5cefc..f30dadffa788 100644 --- a/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml +++ b/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml @@ -15,14 +15,10 @@ --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/activity_confirmation" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:background="@drawable/dialog_background" - android:elevation="16dp" - android:maxHeight="400dp" - android:orientation="vertical" - android:padding="18dp" - android:layout_gravity="center"> + style="@style/ContainerLayout"> + + <!-- A header for selfManaged devices only. --> + <include layout="@layout/vendor_header" /> <!-- Do NOT change the ID of the root LinearLayout above: it's referenced in CTS tests. --> @@ -51,9 +47,9 @@ <androidx.recyclerview.widget.RecyclerView android:id="@+id/device_list" - style="@android:style/Widget.Material.ListView" - android:layout_width="match_parent" - android:layout_height="200dp" /> + android:layout_width="match_parent" + android:scrollbars="vertical" + android:layout_height="200dp" /> </RelativeLayout> diff --git a/packages/CompanionDeviceManager/res/layout/helper_confirmation.xml b/packages/CompanionDeviceManager/res/layout/helper_confirmation.xml new file mode 100644 index 000000000000..8fd1fb0de891 --- /dev/null +++ b/packages/CompanionDeviceManager/res/layout/helper_confirmation.xml @@ -0,0 +1,63 @@ +<!-- + ~ Copyright (C) 2022 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. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/helper_confirmation" + android:theme="@style/ChooserActivity" + style="@style/ContainerLayout"> + + <ImageView + android:id="@+id/app_icon" + android:layout_width="match_parent" + android:layout_height="32dp" + android:gravity="center" + android:layout_marginBottom="12dp" + android:layout_marginTop="1dp"/> + + <TextView + android:id="@+id/helper_title" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="center" + android:paddingHorizontal="12dp" + style="@*android:style/TextAppearance.Widget.Toolbar.Title" + android:textSize="20sp" /> + + <TextView + android:id="@+id/helper_summary" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="12dp" + android:layout_marginLeft="20dp" + android:layout_marginBottom="24dp" + android:gravity="start" + android:textColor="?android:attr/textColorSecondary" + android:textSize="14sp" /> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:gravity="end"> + + <Button + android:id="@+id/btn_ok" + style="@style/VendorHelperOkButton" + android:text="@string/consent_ok" /> + + </LinearLayout> + +</LinearLayout> diff --git a/packages/CompanionDeviceManager/res/layout/vendor_header.xml b/packages/CompanionDeviceManager/res/layout/vendor_header.xml new file mode 100644 index 000000000000..d04eadfb62f4 --- /dev/null +++ b/packages/CompanionDeviceManager/res/layout/vendor_header.xml @@ -0,0 +1,48 @@ +<!-- + ~ Copyright (C) 2022 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. + --> + +<RelativeLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/vendor_header" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:layout_gravity="center" + android:layout_marginBottom="16dp" + android:visibility="gone" > + + <ImageView + android:id="@+id/vendor_header_image" + android:layout_width="31dp" + android:layout_height="32dp" /> + + <TextView + android:id="@+id/vendor_header_name" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginLeft="20dp" + android:layout_marginTop="5dp" + android:layout_toRightOf="@+id/header_image" /> + + <ImageButton + android:id="@+id/vendor_header_button" + style="?android:attr/actionOverflowButtonStyle" + android:layout_width="31dp" + android:layout_height="32dp" + android:layout_marginLeft="100dp" + android:layout_alignParentRight="true" /> + +</RelativeLayout>
\ No newline at end of file diff --git a/packages/CompanionDeviceManager/res/values/strings.xml b/packages/CompanionDeviceManager/res/values/strings.xml index f32f2cd80c47..9626751679e1 100644 --- a/packages/CompanionDeviceManager/res/values/strings.xml +++ b/packages/CompanionDeviceManager/res/values/strings.xml @@ -39,7 +39,7 @@ <!-- ================= DEVICE_PROFILE_APP_STREAMING ================= --> <!-- Confirmation for associating an application with a companion device of APP_STREAMING profile (type) [CHAR LIMIT=NONE] --> - <string name="title_app_streaming">Allow <strong><xliff:g id="app_name" example="Exo">%1$s</xliff:g></strong> to stream applications?</string> + <string name="title_app_streaming">Allow <strong><xliff:g id="app_name" example="Exo">%1$s</xliff:g></strong> to access this information for your phone</string> <!-- Description of the privileges the application will get if associated with the companion device of APP_STREAMING profile (type) [CHAR LIMIT=NONE] --> <string name="summary_app_streaming" product="default">Let <strong><xliff:g id="app_name" example="Exo">%1$s</xliff:g></strong> to provide <strong><xliff:g id="device_name" example="Pixelbook Go">%2$s</xliff:g></strong> remote access to access to applications installed on this phone when connected.</string> @@ -50,6 +50,12 @@ <!-- Description of the privileges the application will get if associated with the companion device of APP_STREAMING profile (type) [CHAR LIMIT=NONE] --> <string name="summary_app_streaming" product="device">Let <strong><xliff:g id="app_name" example="Exo">%1$s</xliff:g></strong> to provide <strong><xliff:g id="device_name" example="Pixelbook Go">%2$s</xliff:g></strong> remote access to access to applications installed on this device when connected.</string> + <!-- Title of the helper dialog for APP_STREAMING profile [CHAR LIMIT=30]. --> + <string name="helper_title_app_streaming">Cross-device services</string> + + <!-- Description of the helper dialog for APP_STREAMING profile. [CHAR LIMIT=NONE] --> + <string name="helper_summary_app_streaming">This service is used to stream apps between your devices</string> + <!-- ================= DEVICE_PROFILE_AUTOMOTIVE_PROJECTION ================= --> <!-- Confirmation for associating an application with a companion device of AUTOMOTIVE_PROJECTION profile (type) [CHAR LIMIT=NONE] --> @@ -66,6 +72,15 @@ <!-- Description of the privileges the application will get if associated with the companion device of COMPUTER profile (type) [CHAR LIMIT=NONE] --> <string name="summary_computer"></string> + <!-- Title of the helper dialog for COMPUTER profile [CHAR LIMIT=30]. --> + <string name="helper_title_computer">Google Play services</string> + + <!-- Description of the helper dialog for COMPUTER profile. [CHAR LIMIT=NONE] --> + <string name="helper_summary_computer" product="default">This service shares photos, media, and notifications form your phone to other devices</string> + + <!-- Description of the helper dialog for COMPUTER profile. [CHAR LIMIT=NONE] --> + <string name="helper_summary_computer" product="tablet">This service shares photos, media, and notifications form your phone to other devices</string> + <!-- ================= null profile ================= --> <!-- A noun for a companion device with unspecified profile (type) [CHAR LIMIT=30] --> @@ -82,6 +97,9 @@ <!-- Negative button for the device-app association consent dialog [CHAR LIMIT=30] --> <string name="consent_no">Don\u2019t allow</string> + <!-- Ok button for the helper consent dialog [CHAR LIMIT=30] --> + <string name="consent_ok">OK</string> + <!-- ================== System data transfer ==================== --> <!-- Title of the permission sync confirmation dialog. [CHAR LIMIT=60] --> <string name="permission_sync_confirmation_title">Transfer app permissions to your diff --git a/packages/CompanionDeviceManager/res/values/styles.xml b/packages/CompanionDeviceManager/res/values/styles.xml new file mode 100644 index 000000000000..bba45e9ecf0f --- /dev/null +++ b/packages/CompanionDeviceManager/res/values/styles.xml @@ -0,0 +1,38 @@ +<!-- + ~ Copyright (C) 2022 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> + <style name="ContainerLayout"> + <item name="android:padding">18dp</item> + <item name="android:elevation">16dp</item> + <item name="android:maxHeight">400dp</item> + <item name="android:orientation">vertical</item> + <item name="android:layout_gravity">center</item> + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:background">@drawable/dialog_background</item> + </style> + + <style name="VendorHelperOkButton" + parent="@android:style/Widget.Material.Button.Borderless.Colored"> + <item name="android:layout_width">50dp</item> + <item name="android:layout_height">35dp</item> + <item name="android:layout_marginTop">20dp</item> + <item name="android:textColor">@android:color/system_neutral1_900</item> + <item name="android:background">@drawable/helper_ok_button</item> + </style> + +</resources>
\ No newline at end of file diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java index ae0c8ccdf006..0fba250b200a 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java @@ -26,6 +26,8 @@ import static com.android.companiondevicemanager.CompanionDeviceDiscoveryService import static com.android.companiondevicemanager.CompanionDeviceDiscoveryService.DiscoveryState.FINISHED_TIMEOUT; import static com.android.companiondevicemanager.Utils.getApplicationLabel; import static com.android.companiondevicemanager.Utils.getHtmlFromResources; +import static com.android.companiondevicemanager.Utils.getVendorHeaderIcon; +import static com.android.companiondevicemanager.Utils.getVendorHeaderName; import static com.android.companiondevicemanager.Utils.prepareResultReceiverForIpc; import static java.util.Objects.requireNonNull; @@ -38,6 +40,7 @@ import android.companion.CompanionDeviceManager; import android.companion.IAssociationRequestCallback; import android.content.Intent; import android.content.pm.PackageManager; +import android.graphics.drawable.Drawable; import android.net.MacAddress; import android.os.Bundle; import android.os.Handler; @@ -47,9 +50,14 @@ import android.text.Spanned; import android.util.Log; import android.view.View; import android.widget.Button; +import android.widget.ImageButton; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; import android.widget.TextView; import androidx.fragment.app.FragmentActivity; +import androidx.fragment.app.FragmentManager; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -59,7 +67,8 @@ import java.util.List; * A CompanionDevice activity response for showing the available * nearby devices to be associated with. */ -public class CompanionDeviceActivity extends FragmentActivity { +public class CompanionDeviceActivity extends FragmentActivity implements + CompanionVendorHelperDialogFragment.CompanionVendorHelperDialogListener { private static final boolean DEBUG = false; private static final String TAG = CompanionDeviceActivity.class.getSimpleName(); @@ -72,6 +81,8 @@ public class CompanionDeviceActivity extends FragmentActivity { private static final String EXTRA_ASSOCIATION_REQUEST = "association_request"; private static final String EXTRA_RESULT_RECEIVER = "result_receiver"; + private static final String FRAGMENT_DIALOG_TAG = "fragment_dialog"; + // Activity result: Internal Error. private static final int RESULT_INTERNAL_ERROR = 2; @@ -91,6 +102,11 @@ public class CompanionDeviceActivity extends FragmentActivity { private TextView mTitle; private TextView mSummary; + // Only present for selfManaged devices. + private ImageView mVendorHeaderImage; + private TextView mVendorHeaderName; + private ImageButton mVendorHeaderButton; + // Progress indicator is only shown while we are looking for the first suitable device for a // "regular" (ie. not self-managed) association. private View mProgressIndicator; @@ -98,6 +114,11 @@ public class CompanionDeviceActivity extends FragmentActivity { // Present for self-managed association requests and "single-device" regular association // regular. private Button mButtonAllow; + // Present for all associations. + private Button mButtonNotAllow; + + private LinearLayout mAssociationConfirmationDialog; + private RelativeLayout mVendorHeader; // The recycler view is only shown for multiple-device regular association request, after // at least one matching device is found. @@ -211,15 +232,25 @@ public class CompanionDeviceActivity extends FragmentActivity { setContentView(R.layout.activity_confirmation); + mAssociationConfirmationDialog = findViewById(R.id.activity_confirmation); + mVendorHeader = findViewById(R.id.vendor_header); + mTitle = findViewById(R.id.title); mSummary = findViewById(R.id.summary); + mVendorHeaderImage = findViewById(R.id.vendor_header_image); + mVendorHeaderName = findViewById(R.id.vendor_header_name); + mVendorHeaderButton = findViewById(R.id.vendor_header_button); + mRecyclerView = findViewById(R.id.device_list); mAdapter = new DeviceListAdapter(this, this::onListItemClick); mButtonAllow = findViewById(R.id.btn_positive); + mButtonNotAllow = findViewById(R.id.btn_negative); + mButtonAllow.setOnClickListener(this::onPositiveButtonClick); - findViewById(R.id.btn_negative).setOnClickListener(this::onNegativeButtonClick); + mButtonNotAllow.setOnClickListener(this::onNegativeButtonClick); + mVendorHeaderButton.setOnClickListener(this::onShowHelperDialog); if (mRequest.isSelfManaged()) { initUiForSelfManagedAssociation(appLabel); @@ -321,11 +352,24 @@ public class CompanionDeviceActivity extends FragmentActivity { private void initUiForSelfManagedAssociation(CharSequence appLabel) { if (DEBUG) Log.i(TAG, "initUiFor_SelfManaged_Association()"); - final CharSequence deviceName = mRequest.getDisplayName(); // "<device>"; - final String deviceProfile = mRequest.getDeviceProfile(); // DEVICE_PROFILE_APP_STREAMING; - + final CharSequence deviceName = mRequest.getDisplayName(); + final String deviceProfile = mRequest.getDeviceProfile(); + final String packageName = mRequest.getPackageName(); + final int userId = mRequest.getUserId(); + final Drawable vendorIcon; + final CharSequence vendorName; final Spanned title; final Spanned summary; + + try { + vendorIcon = getVendorHeaderIcon(this, packageName, userId); + vendorName = getVendorHeaderName(this, packageName, userId); + } catch (PackageManager.NameNotFoundException e) { + Log.e(TAG, "Package u" + userId + "/" + packageName + " not found."); + setResultAndFinish(null, RESULT_INTERNAL_ERROR); + return; + } + switch (deviceProfile) { case DEVICE_PROFILE_APP_STREAMING: title = getHtmlFromResources(this, R.string.title_app_streaming, appLabel); @@ -348,10 +392,14 @@ public class CompanionDeviceActivity extends FragmentActivity { default: throw new RuntimeException("Unsupported profile " + deviceProfile); } + mTitle.setText(title); mSummary.setText(summary); + mVendorHeaderImage.setImageDrawable(vendorIcon); + mVendorHeaderName.setText(vendorName); mRecyclerView.setVisibility(View.GONE); + mVendorHeader.setVisibility(View.VISIBLE); } private void initUiForSingleDevice(CharSequence appLabel) { @@ -370,6 +418,7 @@ public class CompanionDeviceActivity extends FragmentActivity { String deviceProfile, CharSequence appLabel) { // Ignore "empty" scan reports. if (deviceFilterPairs.isEmpty()) return; + mSelectedDevice = requireNonNull(deviceFilterPairs.get(0)); final String deviceName = mSelectedDevice.getDisplayName(); @@ -464,6 +513,17 @@ public class CompanionDeviceActivity extends FragmentActivity { cancel(false); } + private void onShowHelperDialog(View view) { + FragmentManager fragmentManager = getSupportFragmentManager(); + CompanionVendorHelperDialogFragment fragmentDialog = + CompanionVendorHelperDialogFragment.newInstance(mRequest.getPackageName(), + mRequest.getUserId(), mRequest.getDeviceProfile()); + + mAssociationConfirmationDialog.setVisibility(View.GONE); + + fragmentDialog.show(fragmentManager, /* Tag */ FRAGMENT_DIALOG_TAG); + } + private boolean isDone() { return mApproved || mCancelled; } @@ -482,4 +542,14 @@ public class CompanionDeviceActivity extends FragmentActivity { onAssociationCreated(association); } }; + + @Override + public void onShowHelperDialogFailed() { + setResultAndFinish(null, RESULT_INTERNAL_ERROR); + } + + @Override + public void onHelperDialogDismissed() { + mAssociationConfirmationDialog.setVisibility(View.VISIBLE); + } } diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionVendorHelperDialogFragment.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionVendorHelperDialogFragment.java new file mode 100644 index 000000000000..728e5e5d3b9e --- /dev/null +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionVendorHelperDialogFragment.java @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2022 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.companiondevicemanager; + +import static android.companion.AssociationRequest.DEVICE_PROFILE_APP_STREAMING; +import static android.companion.AssociationRequest.DEVICE_PROFILE_COMPUTER; + +import static com.android.companiondevicemanager.Utils.getApplicationIcon; +import static com.android.companiondevicemanager.Utils.getHtmlFromResources; + +import android.annotation.Nullable; +import android.content.DialogInterface; +import android.content.pm.PackageManager; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.text.Spanned; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.fragment.app.DialogFragment; + +/** + * A fragmentDialog shows additional information about selfManaged devices + */ +public class CompanionVendorHelperDialogFragment extends DialogFragment { + private static final String TAG = CompanionVendorHelperDialogFragment.class.getSimpleName(); + + private static final String PACKAGE_NAME_EXTRA = "packageName"; + private static final String DEVICE_PROFILE_EXTRA = "deviceProfile"; + private static final String USER_ID_EXTRA = "userId"; + + private CompanionVendorHelperDialogListener mListener; + // Only present for selfManaged devices. + private TextView mTitle; + private TextView mSummary; + private ImageView mAppIcon; + private Button mButton; + + interface CompanionVendorHelperDialogListener { + void onShowHelperDialogFailed(); + void onHelperDialogDismissed(); + } + + private CompanionVendorHelperDialogFragment() {} + + static CompanionVendorHelperDialogFragment newInstance(String packageName, + int userId, String deviceProfile) { + CompanionVendorHelperDialogFragment fragmentDialog = + new CompanionVendorHelperDialogFragment(); + + Bundle bundle = new Bundle(); + bundle.putString(PACKAGE_NAME_EXTRA, packageName); + bundle.putInt(USER_ID_EXTRA, userId); + bundle.putString(DEVICE_PROFILE_EXTRA, deviceProfile); + fragmentDialog.setArguments(bundle); + + return fragmentDialog; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mListener = (CompanionVendorHelperDialogListener) getActivity(); + // Hide the title bar in the dialog. + setStyle(STYLE_NO_TITLE, /* Theme */0); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + return inflater.inflate(R.layout.helper_confirmation, container); + } + + @Override + public void onDismiss(@NonNull DialogInterface dialog) { + super.onDismiss(dialog); + mListener.onHelperDialogDismissed(); + } + + @Override + public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + Drawable applicationIcon; + String packageName = getArguments().getString(PACKAGE_NAME_EXTRA); + String deviceProfile = getArguments().getString(DEVICE_PROFILE_EXTRA); + int userId = getArguments().getInt(USER_ID_EXTRA); + + try { + applicationIcon = getApplicationIcon(getContext(), packageName); + } catch (PackageManager.NameNotFoundException e) { + Log.e(TAG, "Package u" + userId + "/" + packageName + " not found."); + mListener.onShowHelperDialogFailed(); + return; + } + + mTitle = view.findViewById(R.id.helper_title); + mSummary = view.findViewById(R.id.helper_summary); + mAppIcon = view.findViewById(R.id.app_icon); + mButton = view.findViewById(R.id.btn_ok); + + final Spanned title; + final Spanned summary; + + switch (deviceProfile) { + case DEVICE_PROFILE_APP_STREAMING: + title = getHtmlFromResources(getContext(), R.string.helper_title_app_streaming); + summary = getHtmlFromResources(getContext(), R.string.helper_summary_app_streaming); + break; + + case DEVICE_PROFILE_COMPUTER: + title = getHtmlFromResources(getContext(), R.string.helper_title_computer); + summary = getHtmlFromResources(getContext(), R.string.helper_summary_computer); + break; + + default: + throw new RuntimeException("Unsupported profile " + deviceProfile); + } + + mTitle.setText(title); + mSummary.setText(summary); + mAppIcon.setImageDrawable(applicationIcon); + + mButton.setOnClickListener(v -> { + dismiss(); + mListener.onHelperDialogDismissed(); + }); + } +} diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/Utils.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/Utils.java index e3e563d56e8a..76bbcfb79155 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/Utils.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/Utils.java @@ -21,6 +21,9 @@ import android.annotation.StringRes; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.ApplicationInfoFlags; +import android.graphics.drawable.Drawable; +import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Parcel; @@ -32,6 +35,10 @@ import android.text.Spanned; * Utilities. */ class Utils { + private static final String COMPANION_DEVICE_ACTIVITY_VENDOR_ICON = + "android.companion.vendor_icon"; + private static final String COMPANION_DEVICE_ACTIVITY_VENDOR_NAME = + "android.companion.vendor_name"; /** * Convert an instance of a "locally-defined" ResultReceiver to an instance of @@ -60,6 +67,12 @@ class Utils { return packageManager.getApplicationLabel(appInfo); } + static @NonNull Drawable getApplicationIcon(@NonNull Context context, + @NonNull String packageName) throws PackageManager.NameNotFoundException { + final PackageManager packageManager = context.getPackageManager(); + return packageManager.getApplicationIcon(packageName); + } + static Spanned getHtmlFromResources( @NonNull Context context, @StringRes int resId, CharSequence... formatArgs) { final String[] escapedArgs = new String[formatArgs.length]; @@ -70,6 +83,44 @@ class Utils { return Html.fromHtml(plain, 0); } + static @NonNull Drawable getVendorHeaderIcon(@NonNull Context context, + @NonNull String packageName, int userId) throws PackageManager.NameNotFoundException { + final ApplicationInfo appInfo = getApplicationInfo(context, packageName, userId); + final Bundle bundle = appInfo.metaData; + int resId = bundle == null ? 0 : bundle.getInt(COMPANION_DEVICE_ACTIVITY_VENDOR_ICON, 0); + + if (bundle == null || resId == 0) { + return getApplicationIcon(context, packageName); + } + + return context.createPackageContext(packageName, /* flags= */ 0).getDrawable(resId); + } + + static CharSequence getVendorHeaderName(@NonNull Context context, + @NonNull String packageName, int userId) throws PackageManager.NameNotFoundException { + final ApplicationInfo appInfo = getApplicationInfo(context, packageName, userId); + final Bundle bundle = appInfo.metaData; + + if (bundle == null) { + return ""; + } + + return appInfo.metaData.getCharSequence(COMPANION_DEVICE_ACTIVITY_VENDOR_NAME, ""); + } + + /** + * Getting ApplicationInfo from meta-data. + */ + private static @NonNull ApplicationInfo getApplicationInfo(@NonNull Context context, + @NonNull String packageName, int userId) throws PackageManager.NameNotFoundException { + final PackageManager packageManager = context.getPackageManager(); + final ApplicationInfoFlags flags = ApplicationInfoFlags.of(PackageManager.GET_META_DATA); + final ApplicationInfo appInfo = packageManager.getApplicationInfoAsUser( + packageName, flags, userId); + + return appInfo; + } + static void runOnMainThread(Runnable runnable) { if (Thread.currentThread() == Looper.getMainLooper().getThread()) { runnable.run(); |