diff --git a/packages/CompanionDeviceManager/res/drawable/ic_apps.xml b/packages/CompanionDeviceManager/res/drawable/ic_apps.xml new file mode 100644 index 0000000000000..f839b80d2e0c5 --- /dev/null +++ b/packages/CompanionDeviceManager/res/drawable/ic_apps.xml @@ -0,0 +1,34 @@ + + + + + + + + \ No newline at end of file diff --git a/packages/CompanionDeviceManager/res/drawable/ic_notifications.xml b/packages/CompanionDeviceManager/res/drawable/ic_notifications.xml new file mode 100644 index 0000000000000..4ac4d04b184e7 --- /dev/null +++ b/packages/CompanionDeviceManager/res/drawable/ic_notifications.xml @@ -0,0 +1,25 @@ + + + + + \ No newline at end of file diff --git a/packages/CompanionDeviceManager/res/drawable/ic_storage.xml b/packages/CompanionDeviceManager/res/drawable/ic_storage.xml new file mode 100644 index 0000000000000..d8b7f59185c85 --- /dev/null +++ b/packages/CompanionDeviceManager/res/drawable/ic_storage.xml @@ -0,0 +1,25 @@ + + + + + \ 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 f30dadffa7884..82fe72bee4e49 100644 --- a/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml +++ b/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml @@ -51,6 +51,11 @@ android:scrollbars="vertical" android:layout_height="200dp" /> + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/CompanionDeviceManager/res/values/strings.xml b/packages/CompanionDeviceManager/res/values/strings.xml index 9626751679e15..c4ad432b092ee 100644 --- a/packages/CompanionDeviceManager/res/values/strings.xml +++ b/packages/CompanionDeviceManager/res/values/strings.xml @@ -38,6 +38,12 @@ + + Apps + + + Stream your phone\u2019s apps + Allow <strong>%1$s</strong> to access this information for your phone @@ -72,6 +78,18 @@ + + Notifications + + + Can read all notifications, including information like contracts, messages, and photos + + + Photos and media + + + + Google Play services diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java index 0fba250b200ad..1b626911fdde0 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java @@ -24,6 +24,9 @@ import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTE import static com.android.companiondevicemanager.CompanionDeviceDiscoveryService.DiscoveryState; import static com.android.companiondevicemanager.CompanionDeviceDiscoveryService.DiscoveryState.FINISHED_TIMEOUT; +import static com.android.companiondevicemanager.PermissionListAdapter.TYPE_APPS; +import static com.android.companiondevicemanager.PermissionListAdapter.TYPE_NOTIFICATION; +import static com.android.companiondevicemanager.PermissionListAdapter.TYPE_STORAGE; import static com.android.companiondevicemanager.Utils.getApplicationLabel; import static com.android.companiondevicemanager.Utils.getHtmlFromResources; import static com.android.companiondevicemanager.Utils.getVendorHeaderIcon; @@ -61,6 +64,7 @@ import androidx.fragment.app.FragmentManager; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; +import java.util.ArrayList; import java.util.List; /** @@ -122,8 +126,12 @@ public class CompanionDeviceActivity extends FragmentActivity implements // The recycler view is only shown for multiple-device regular association request, after // at least one matching device is found. - private @Nullable RecyclerView mRecyclerView; - private @Nullable DeviceListAdapter mAdapter; + private @Nullable RecyclerView mDeviceListRecyclerView; + private @Nullable DeviceListAdapter mDeviceAdapter; + + // The recycler view is only shown for selfManaged association request. + private @Nullable RecyclerView mPermissionListRecyclerView; + private @Nullable PermissionListAdapter mPermissionListAdapter; // The flag used to prevent double taps, that may lead to sending several requests for creating // an association to CDM. @@ -133,6 +141,8 @@ public class CompanionDeviceActivity extends FragmentActivity implements // onActivityResult() after the association is created. private @Nullable DeviceFilterPair mSelectedDevice; + private @Nullable List mPermissionTypes; + @Override public void onCreate(Bundle savedInstanceState) { if (DEBUG) Log.d(TAG, "onCreate()"); @@ -242,8 +252,11 @@ public class CompanionDeviceActivity extends FragmentActivity implements 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); + mDeviceListRecyclerView = findViewById(R.id.device_list); + mDeviceAdapter = new DeviceListAdapter(this, this::onListItemClick); + + mPermissionListRecyclerView = findViewById(R.id.permission_list); + mPermissionListAdapter = new PermissionListAdapter(this); mButtonAllow = findViewById(R.id.btn_positive); mButtonNotAllow = findViewById(R.id.btn_negative); @@ -359,7 +372,8 @@ public class CompanionDeviceActivity extends FragmentActivity implements final Drawable vendorIcon; final CharSequence vendorName; final Spanned title; - final Spanned summary; + + mPermissionTypes = new ArrayList<>(); try { vendorIcon = getVendorHeaderIcon(this, packageName, userId); @@ -372,33 +386,36 @@ public class CompanionDeviceActivity extends FragmentActivity implements switch (deviceProfile) { case DEVICE_PROFILE_APP_STREAMING: - title = getHtmlFromResources(this, R.string.title_app_streaming, appLabel); - summary = getHtmlFromResources( - this, R.string.summary_app_streaming, appLabel, deviceName); + title = getHtmlFromResources(this, R.string.title_app_streaming, deviceName); + mPermissionTypes.add(TYPE_APPS); break; case DEVICE_PROFILE_AUTOMOTIVE_PROJECTION: - title = getHtmlFromResources(this, R.string.title_automotive_projection, appLabel); - summary = getHtmlFromResources( - this, R.string.summary_automotive_projection, appLabel, deviceName); + title = getHtmlFromResources( + this, R.string.title_automotive_projection, deviceName); break; case DEVICE_PROFILE_COMPUTER: - title = getHtmlFromResources(this, R.string.title_computer, appLabel); - summary = getHtmlFromResources( - this, R.string.summary_computer, appLabel, deviceName); + title = getHtmlFromResources(this, R.string.title_computer, deviceName); + mPermissionTypes.add(TYPE_NOTIFICATION); + mPermissionTypes.add(TYPE_STORAGE); break; default: throw new RuntimeException("Unsupported profile " + deviceProfile); } + mSummary.setVisibility(View.GONE); + + mPermissionListAdapter.setPermissionType(mPermissionTypes); + mPermissionListRecyclerView.setAdapter(mPermissionListAdapter); + mPermissionListRecyclerView.setLayoutManager(new LinearLayoutManager(this)); + mTitle.setText(title); - mSummary.setText(summary); mVendorHeaderImage.setImageDrawable(vendorIcon); mVendorHeaderName.setText(vendorName); - mRecyclerView.setVisibility(View.GONE); + mDeviceListRecyclerView.setVisibility(View.GONE); mVendorHeader.setVisibility(View.VISIBLE); } @@ -411,7 +428,8 @@ public class CompanionDeviceActivity extends FragmentActivity implements deviceFilterPairs -> updateSingleDeviceUi( deviceFilterPairs, deviceProfile, appLabel)); - mRecyclerView.setVisibility(View.GONE); + mPermissionListRecyclerView.setVisibility(View.GONE); + mDeviceListRecyclerView.setVisibility(View.GONE); } private void updateSingleDeviceUi(List> deviceFilterPairs, @@ -460,15 +478,15 @@ public class CompanionDeviceActivity extends FragmentActivity implements mTitle.setText(title); mSummary.setText(summary); - mAdapter = new DeviceListAdapter(this, this::onListItemClick); + mDeviceAdapter = new DeviceListAdapter(this, this::onListItemClick); // TODO: hide the list and show a spinner until a first device matching device is found. - mRecyclerView.setAdapter(mAdapter); - mRecyclerView.setLayoutManager(new LinearLayoutManager(this)); + mDeviceListRecyclerView.setAdapter(mDeviceAdapter); + mDeviceListRecyclerView.setLayoutManager(new LinearLayoutManager(this)); CompanionDeviceDiscoveryService.getScanResult().observe( /* lifecycleOwner */ this, - /* observer */ mAdapter); + /* observer */ mDeviceAdapter); // "Remove" consent button: users would need to click on the list item. mButtonAllow.setVisibility(View.GONE); @@ -477,14 +495,14 @@ public class CompanionDeviceActivity extends FragmentActivity implements private void onListItemClick(int position) { if (DEBUG) Log.d(TAG, "onListItemClick() " + position); - final DeviceFilterPair selectedDevice = mAdapter.getItem(position); + final DeviceFilterPair selectedDevice = mDeviceAdapter.getItem(position); if (mSelectedDevice != null) { if (DEBUG) Log.w(TAG, "Already selected."); return; } // Notify the adapter to highlight the selected item. - mAdapter.setSelectedPosition(position); + mDeviceAdapter.setSelectedPosition(position); mSelectedDevice = requireNonNull(selectedDevice); diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceListAdapter.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceListAdapter.java index e5513b0748651..8babd3ade1eb5 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceListAdapter.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceListAdapter.java @@ -15,9 +15,10 @@ */ package com.android.companiondevicemanager; + +import static com.android.companiondevicemanager.Utils.getIcon; + import android.content.Context; -import android.graphics.Color; -import android.graphics.drawable.Drawable; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -60,11 +61,11 @@ class DeviceListAdapter extends RecyclerView.Adapter { + private final Context mContext; + + private List mPermissions; + + static final int TYPE_NOTIFICATION = 0; + static final int TYPE_STORAGE = 1; + static final int TYPE_APPS = 2; + + private static final Map sTitleMap; + static { + final Map map = new ArrayMap<>(); + map.put(TYPE_NOTIFICATION, R.string.permission_notification); + map.put(TYPE_STORAGE, R.string.permission_storage); + map.put(TYPE_APPS, R.string.permission_apps); + sTitleMap = unmodifiableMap(map); + } + + private static final Map sSummaryMap; + static { + final Map map = new ArrayMap<>(); + map.put(TYPE_NOTIFICATION, R.string.permission_notification_summary); + map.put(TYPE_STORAGE, R.string.permission_storage_summary); + map.put(TYPE_APPS, R.string.permission_apps_summary); + sSummaryMap = unmodifiableMap(map); + } + + private static final Map sIconMap; + static { + final Map map = new ArrayMap<>(); + map.put(TYPE_NOTIFICATION, R.drawable.ic_notifications); + map.put(TYPE_STORAGE, R.drawable.ic_storage); + map.put(TYPE_APPS, R.drawable.ic_apps); + sIconMap = unmodifiableMap(map); + } + + PermissionListAdapter(Context context) { + mContext = context; + } + + @Override + public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + View view = LayoutInflater.from(parent.getContext()).inflate( + R.layout.list_item_permission, parent, false); + ViewHolder viewHolder = new ViewHolder(view); + viewHolder.mPermissionIcon.setImageDrawable(getIcon(mContext, sIconMap.get(viewType))); + + return viewHolder; + } + + @Override + public void onBindViewHolder(ViewHolder holder, int position) { + int type = getItemViewType(position); + final Spanned title = getHtmlFromResources(mContext, sTitleMap.get(type)); + final Spanned summary = getHtmlFromResources(mContext, sSummaryMap.get(type)); + + holder.mPermissionName.setText(title); + holder.mPermissionSummary.setText(summary); + } + + @Override + public int getItemViewType(int position) { + return mPermissions.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public int getItemCount() { + return mPermissions != null ? mPermissions.size() : 0; + } + + static class ViewHolder extends RecyclerView.ViewHolder { + private final TextView mPermissionName; + private final TextView mPermissionSummary; + private final ImageView mPermissionIcon; + ViewHolder(View itemView) { + super(itemView); + mPermissionName = itemView.findViewById(R.id.permission_name); + mPermissionSummary = itemView.findViewById(R.id.permission_summary); + mPermissionIcon = itemView.findViewById(R.id.permission_icon); + } + } + + void setPermissionType(List permissions) { + mPermissions = permissions; + } +} diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/Utils.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/Utils.java index 76bbcfb791557..d5b2f0a748f1b 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/Utils.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/Utils.java @@ -22,6 +22,7 @@ import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.ApplicationInfoFlags; +import android.graphics.Color; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.Handler; @@ -121,6 +122,12 @@ class Utils { return appInfo; } + static @NonNull Drawable getIcon(@NonNull Context context, int resId) { + Drawable icon = context.getResources().getDrawable(resId, null); + icon.setTint(Color.DKGRAY); + return icon; + } + static void runOnMainThread(Runnable runnable) { if (Thread.currentThread() == Looper.getMainLooper().getThread()) { runnable.run();