Loading packages/CompanionDeviceManager/res/layout/activity_confirmation.xml +7 −3 Original line number Diff line number Diff line Loading @@ -14,6 +14,7 @@ limitations under the License. --> <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" Loading @@ -23,6 +24,8 @@ android:padding="18dp" android:layout_gravity="center"> <!-- Do NOT change the ID of the root LinearLayout above: it's referenced in CTS tests. --> <TextView android:id="@+id/title" android:layout_width="match_parent" Loading @@ -30,7 +33,6 @@ android:gravity="center" android:paddingHorizontal="12dp" style="@*android:style/TextAppearance.Widget.Toolbar.Title"/> <!-- style="@*android:style/TextAppearance.Widget.Toolbar.Title" --> <TextView android:id="@+id/summary" Loading Loading @@ -61,8 +63,10 @@ android:orientation="horizontal" android:gravity="end"> <!-- Do NOT change the IDs of the buttons: they are referenced in CTS tests. --> <Button android:id="@+id/button_cancel" android:id="@+id/btn_negative" style="@android:style/Widget.Material.Button.Borderless.Colored" android:layout_width="wrap_content" android:layout_height="wrap_content" Loading @@ -70,7 +74,7 @@ android:textColor="?android:attr/textColorSecondary" /> <Button android:id="@+id/button_allow" android:id="@+id/btn_positive" style="@android:style/Widget.Material.Button.Borderless.Colored" android:layout_width="wrap_content" android:layout_height="wrap_content" Loading packages/CompanionDeviceManager/res/layout/list_item_device.xml 0 → 100644 +39 −0 Original line number Diff line number Diff line <?xml version="1.0" encoding="utf-8"?> <!-- Copyright (C) 2021 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/list_item_device" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:gravity="center_vertical" android:padding="12dp"> <!-- Do NOT change the ID of the root LinearLayout above: it's referenced in CTS tests. --> <ImageView android:id="@android:id/icon" android:layout_width="24dp" android:layout_height="24dp" android:layout_marginRight="12dp"/> <TextView android:id="@android:id/text1" android:layout_width="match_parent" android:layout_height="wrap_content" android:singleLine="true" android:textAppearance="?android:attr/textAppearanceListItemSmall"/> </LinearLayout> No newline at end of file packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java +94 −45 Original line number Diff line number Diff line Loading @@ -21,6 +21,8 @@ import static android.companion.AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PRO import static android.companion.AssociationRequest.DEVICE_PROFILE_WATCH; import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; import static com.android.companiondevicemanager.CompanionDeviceDiscoveryService.SCAN_RESULTS_OBSERVABLE; import static com.android.companiondevicemanager.CompanionDeviceDiscoveryService.TIMEOUT_OBSERVABLE; import static com.android.companiondevicemanager.Utils.getApplicationLabel; import static com.android.companiondevicemanager.Utils.getHtmlFromResources; import static com.android.companiondevicemanager.Utils.prepareResultReceiverForIpc; Loading Loading @@ -91,10 +93,13 @@ public class CompanionDeviceActivity extends Activity { // The flag used to prevent double taps, that may lead to sending several requests for creating // an association to CDM. private boolean mAssociationApproved; private boolean mApproved; private boolean mCancelled; @Override public void onCreate(Bundle savedInstanceState) { if (DEBUG) Log.d(TAG, "onCreate()"); super.onCreate(savedInstanceState); getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); } Loading @@ -117,25 +122,12 @@ public class CompanionDeviceActivity extends Activity { // Start discovery services if needed. if (!mRequest.isSelfManaged()) { CompanionDeviceDiscoveryService.startForRequest(this, mRequest); TIMEOUT_OBSERVABLE.addObserver((o, arg) -> cancel(true)); } // Init UI. initUI(); } @Override protected void onStop() { super.onStop(); if (DEBUG) Log.d(TAG, "onStop(), finishing=" + isFinishing()); // TODO: handle config changes without cancelling. if (!isFinishing()) { cancel(); // will finish() } // mAdapter may be observing - need to remove it. CompanionDeviceDiscoveryService.SCAN_RESULTS_OBSERVABLE.deleteObservers(); } @Override protected void onNewIntent(Intent intent) { // Handle another incoming request (while we are not done with the original - mRequest - Loading @@ -153,6 +145,39 @@ public class CompanionDeviceActivity extends Activity { } } @Override protected void onStop() { super.onStop(); if (DEBUG) Log.d(TAG, "onStop(), finishing=" + isFinishing()); // TODO: handle config changes without cancelling. if (!isDone()) { cancel(false); // will finish() } TIMEOUT_OBSERVABLE.deleteObservers(); // mAdapter may also be observing - need to remove it. SCAN_RESULTS_OBSERVABLE.deleteObservers(); } @Override protected void onDestroy() { super.onDestroy(); if (DEBUG) Log.d(TAG, "onDestroy()"); } @Override public void onBackPressed() { if (DEBUG) Log.d(TAG, "onBackPressed()"); super.onBackPressed(); } @Override public void finish() { if (DEBUG) Log.d(TAG, "finish()", new Exception("Stack Trace Dump")); super.finish(); } private void initUI() { if (DEBUG) Log.d(TAG, "initUI(), request=" + mRequest); Loading @@ -164,10 +189,9 @@ public class CompanionDeviceActivity extends Activity { mListView = findViewById(R.id.device_list); mListView.setOnItemClickListener((av, iv, position, id) -> onListItemClick(position)); mButtonAllow = findViewById(R.id.button_allow); mButtonAllow.setOnClickListener(this::onAllowButtonClick); findViewById(R.id.button_cancel).setOnClickListener(v -> cancel()); mButtonAllow = findViewById(R.id.btn_positive); mButtonAllow.setOnClickListener(this::onPositiveButtonClick); findViewById(R.id.btn_negative).setOnClickListener(this::onNegativeButtonClick); final CharSequence appLabel = getApplicationLabel(this, mRequest.getPackageName()); if (mRequest.isSelfManaged()) { Loading @@ -179,6 +203,33 @@ public class CompanionDeviceActivity extends Activity { } } private void onAssociationApproved(@Nullable MacAddress macAddress) { if (isDone()) { if (DEBUG) Log.w(TAG, "Already done: " + (mApproved ? "Approved" : "Cancelled")); return; } mApproved = true; if (DEBUG) Log.i(TAG, "onAssociationApproved() macAddress=" + macAddress); if (!mRequest.isSelfManaged()) { requireNonNull(macAddress); CompanionDeviceDiscoveryService.stop(this); } final Bundle data = new Bundle(); data.putParcelable(EXTRA_ASSOCIATION_REQUEST, mRequest); data.putBinder(EXTRA_APPLICATION_CALLBACK, mAppCallback.asBinder()); if (macAddress != null) { data.putParcelable(EXTRA_MAC_ADDRESS, macAddress); } data.putParcelable(EXTRA_RESULT_RECEIVER, prepareResultReceiverForIpc(mOnAssociationCreatedReceiver)); mCdmServiceReceiver.send(RESULT_CODE_ASSOCIATION_APPROVED, data); } private void onAssociationCreated(@NonNull AssociationInfo association) { if (DEBUG) Log.i(TAG, "onAssociationCreated(), association=" + association); Loading @@ -186,17 +237,26 @@ public class CompanionDeviceActivity extends Activity { setResultAndFinish(association); } private void cancel() { if (DEBUG) Log.i(TAG, "cancel()"); private void cancel(boolean discoveryTimeout) { if (DEBUG) { Log.i(TAG, "cancel(), discoveryTimeout=" + discoveryTimeout, new Exception("Stack Trace Dump")); } if (isDone()) { if (DEBUG) Log.w(TAG, "Already done: " + (mApproved ? "Approved" : "Cancelled")); return; } mCancelled = true; // Stop discovery service if it was used. if (!mRequest.isSelfManaged()) { if (!mRequest.isSelfManaged() || discoveryTimeout) { CompanionDeviceDiscoveryService.stop(this); } // First send callback to the app directly... try { mAppCallback.onFailure("Cancelled."); mAppCallback.onFailure(discoveryTimeout ? "Timeout." : "Cancelled."); } catch (RemoteException ignore) { } Loading Loading @@ -297,7 +357,7 @@ public class CompanionDeviceActivity extends Activity { mSummary.setText(summary); mAdapter = new DeviceListAdapter(this); CompanionDeviceDiscoveryService.SCAN_RESULTS_OBSERVABLE.addObserver(mAdapter); SCAN_RESULTS_OBSERVABLE.addObserver(mAdapter); // TODO: hide the list and show a spinner until a first device matching device is found. mListView.setAdapter(mAdapter); Loading @@ -313,8 +373,8 @@ public class CompanionDeviceActivity extends Activity { onAssociationApproved(macAddress); } private void onAllowButtonClick(View v) { if (DEBUG) Log.d(TAG, "onAllowButtonClick()"); private void onPositiveButtonClick(View v) { if (DEBUG) Log.d(TAG, "on_Positive_ButtonClick()"); // Disable the button, to prevent more clicks. v.setEnabled(false); Loading @@ -330,28 +390,17 @@ public class CompanionDeviceActivity extends Activity { onAssociationApproved(macAddress); } private void onAssociationApproved(@Nullable MacAddress macAddress) { if (mAssociationApproved) return; mAssociationApproved = true; private void onNegativeButtonClick(View v) { if (DEBUG) Log.d(TAG, "on_Negative_ButtonClick()"); if (DEBUG) Log.i(TAG, "onAssociationApproved() macAddress=" + macAddress); // Disable the button, to prevent more clicks. v.setEnabled(false); if (!mRequest.isSelfManaged()) { requireNonNull(macAddress); CompanionDeviceDiscoveryService.stop(this); cancel(false); } final Bundle data = new Bundle(); data.putParcelable(EXTRA_ASSOCIATION_REQUEST, mRequest); data.putBinder(EXTRA_APPLICATION_CALLBACK, mAppCallback.asBinder()); if (macAddress != null) { data.putParcelable(EXTRA_MAC_ADDRESS, macAddress); } data.putParcelable(EXTRA_RESULT_RECEIVER, prepareResultReceiverForIpc(mOnAssociationCreatedReceiver)); mCdmServiceReceiver.send(RESULT_CODE_ASSOCIATION_APPROVED, data); private boolean isDone() { return mApproved || mCancelled; } private final ResultReceiver mOnAssociationCreatedReceiver = Loading packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java +24 −3 Original line number Diff line number Diff line Loading @@ -22,6 +22,8 @@ import static com.android.internal.util.CollectionUtils.filter; import static com.android.internal.util.CollectionUtils.find; import static com.android.internal.util.CollectionUtils.map; import static java.lang.Math.max; import static java.lang.Math.min; import static java.util.Objects.requireNonNull; import android.annotation.MainThread; Loading Loading @@ -50,6 +52,7 @@ import android.net.wifi.WifiManager; import android.os.Handler; import android.os.IBinder; import android.os.Parcelable; import android.os.SystemProperties; import android.text.TextUtils; import android.util.Log; Loading @@ -64,13 +67,17 @@ public class CompanionDeviceDiscoveryService extends Service { private static final boolean DEBUG = false; private static final String TAG = CompanionDeviceDiscoveryService.class.getSimpleName(); private static final String SYS_PROP_DEBUG_TIMEOUT = "debug.cdm.discovery_timeout"; private static final long TIMEOUT_DEFAULT = 20_000L; // 20 seconds private static final long TIMEOUT_MIN = 1_000L; // 1 sec private static final long TIMEOUT_MAX = 60_000L; // 1 min private static final String ACTION_START_DISCOVERY = "com.android.companiondevicemanager.action.START_DISCOVERY"; private static final String ACTION_STOP_DISCOVERY = "com.android.companiondevicemanager.action.ACTION_STOP_DISCOVERY"; private static final String EXTRA_ASSOCIATION_REQUEST = "association_request"; private static final long SCAN_TIMEOUT = 20_000L; // 20 seconds // TODO: replace with LiveData-s? static final Observable TIMEOUT_OBSERVABLE = new MyObservable(); Loading Loading @@ -180,8 +187,7 @@ public class CompanionDeviceDiscoveryService extends Service { // Start BLE scanning (if needed) mBleScanCallback = startBleScanningIfNeeded(bleFilters, forceStartScanningAll); // Schedule a time-out. Handler.getMain().postDelayed(mTimeoutRunnable, SCAN_TIMEOUT); scheduleTimeout(); } @MainThread Loading Loading @@ -338,6 +344,21 @@ public class CompanionDeviceDiscoveryService extends Service { }); } private void scheduleTimeout() { long timeout = SystemProperties.getLong(SYS_PROP_DEBUG_TIMEOUT, -1); if (timeout <= 0) { // 0 or negative values indicate that the sysprop was never set or should be ignored. timeout = TIMEOUT_DEFAULT; } else { timeout = min(timeout, TIMEOUT_MAX); // should be <= 1 min (TIMEOUT_MAX) timeout = max(timeout, TIMEOUT_MIN); // should be >= 1 sec (TIMEOUT_MIN) } if (DEBUG) Log.d(TAG, "scheduleTimeout(), timeout=" + timeout); Handler.getMain().postDelayed(mTimeoutRunnable, timeout); } private void timeout() { if (DEBUG) Log.i(TAG, "timeout()"); stopDiscoveryAndFinish(); Loading packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceListAdapter.java +24 −55 Original line number Diff line number Diff line Loading @@ -16,18 +16,14 @@ package com.android.companiondevicemanager; import android.annotation.ColorInt; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Color; import android.graphics.drawable.Drawable; import android.util.TypedValue; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.TextView; import java.util.List; Loading @@ -39,51 +35,12 @@ import java.util.Observer; */ class DeviceListAdapter extends BaseAdapter implements Observer { private final Context mContext; private final Resources mResources; private final Drawable mBluetoothIcon; private final Drawable mWifiIcon; private final @ColorInt int mTextColor; // List if pairs (display name, address) private List<DeviceFilterPair<?>> mDevices; DeviceListAdapter(Context context) { mContext = context; mResources = context.getResources(); mBluetoothIcon = getTintedIcon(mResources, android.R.drawable.stat_sys_data_bluetooth); mWifiIcon = getTintedIcon(mResources, com.android.internal.R.drawable.ic_wifi_signal_3); mTextColor = getColor(context, android.R.attr.colorForeground); } @Override public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) { final TextView view = convertView != null ? (TextView) convertView : newView(); bind(view, getItem(position)); return view; } private void bind(TextView textView, DeviceFilterPair<?> item) { textView.setText(item.getDisplayName()); textView.setBackgroundColor(Color.TRANSPARENT); /* textView.setCompoundDrawablesWithIntrinsicBounds( item.getDevice() instanceof android.net.wifi.ScanResult ? mWifiIcon : mBluetoothIcon, null, null, null); textView.getCompoundDrawables()[0].setTint(mTextColor); */ } private TextView newView() { final TextView textView = new TextView(mContext); textView.setTextColor(mTextColor); final int padding = 24; textView.setPadding(padding, padding, padding, padding); //textView.setCompoundDrawablePadding(padding); return textView; } @Override Loading @@ -107,17 +64,29 @@ class DeviceListAdapter extends BaseAdapter implements Observer { notifyDataSetChanged(); } private @ColorInt int getColor(Context context, int attr) { final TypedArray a = context.obtainStyledAttributes(new TypedValue().data, new int[] { attr }); final int color = a.getColor(0, 0); a.recycle(); return color; @Override public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) { final View view = convertView != null ? convertView : LayoutInflater.from(mContext).inflate(R.layout.list_item_device, parent, false); final DeviceFilterPair<?> item = getItem(position); bindView(view, item); return view; } private static Drawable getTintedIcon(Resources resources, int drawableRes) { Drawable icon = resources.getDrawable(drawableRes, null); icon.setTint(Color.DKGRAY); return icon; private void bindView(@NonNull View view, DeviceFilterPair<?> item) { final TextView textView = view.findViewById(android.R.id.text1); textView.setText(item.getDisplayName()); final ImageView iconView = view.findViewById(android.R.id.icon); // TODO(b/211417476): Set either Bluetooth or WiFi icon. iconView.setVisibility(View.GONE); // final int iconRes = isBt ? android.R.drawable.stat_sys_data_bluetooth // : com.android.internal.R.drawable.ic_wifi_signal_3; // final Drawable icon = getTintedIcon(mResources, iconRes); // iconView.setImageDrawable(icon); } } Loading
packages/CompanionDeviceManager/res/layout/activity_confirmation.xml +7 −3 Original line number Diff line number Diff line Loading @@ -14,6 +14,7 @@ limitations under the License. --> <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" Loading @@ -23,6 +24,8 @@ android:padding="18dp" android:layout_gravity="center"> <!-- Do NOT change the ID of the root LinearLayout above: it's referenced in CTS tests. --> <TextView android:id="@+id/title" android:layout_width="match_parent" Loading @@ -30,7 +33,6 @@ android:gravity="center" android:paddingHorizontal="12dp" style="@*android:style/TextAppearance.Widget.Toolbar.Title"/> <!-- style="@*android:style/TextAppearance.Widget.Toolbar.Title" --> <TextView android:id="@+id/summary" Loading Loading @@ -61,8 +63,10 @@ android:orientation="horizontal" android:gravity="end"> <!-- Do NOT change the IDs of the buttons: they are referenced in CTS tests. --> <Button android:id="@+id/button_cancel" android:id="@+id/btn_negative" style="@android:style/Widget.Material.Button.Borderless.Colored" android:layout_width="wrap_content" android:layout_height="wrap_content" Loading @@ -70,7 +74,7 @@ android:textColor="?android:attr/textColorSecondary" /> <Button android:id="@+id/button_allow" android:id="@+id/btn_positive" style="@android:style/Widget.Material.Button.Borderless.Colored" android:layout_width="wrap_content" android:layout_height="wrap_content" Loading
packages/CompanionDeviceManager/res/layout/list_item_device.xml 0 → 100644 +39 −0 Original line number Diff line number Diff line <?xml version="1.0" encoding="utf-8"?> <!-- Copyright (C) 2021 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/list_item_device" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:gravity="center_vertical" android:padding="12dp"> <!-- Do NOT change the ID of the root LinearLayout above: it's referenced in CTS tests. --> <ImageView android:id="@android:id/icon" android:layout_width="24dp" android:layout_height="24dp" android:layout_marginRight="12dp"/> <TextView android:id="@android:id/text1" android:layout_width="match_parent" android:layout_height="wrap_content" android:singleLine="true" android:textAppearance="?android:attr/textAppearanceListItemSmall"/> </LinearLayout> No newline at end of file
packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java +94 −45 Original line number Diff line number Diff line Loading @@ -21,6 +21,8 @@ import static android.companion.AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PRO import static android.companion.AssociationRequest.DEVICE_PROFILE_WATCH; import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; import static com.android.companiondevicemanager.CompanionDeviceDiscoveryService.SCAN_RESULTS_OBSERVABLE; import static com.android.companiondevicemanager.CompanionDeviceDiscoveryService.TIMEOUT_OBSERVABLE; import static com.android.companiondevicemanager.Utils.getApplicationLabel; import static com.android.companiondevicemanager.Utils.getHtmlFromResources; import static com.android.companiondevicemanager.Utils.prepareResultReceiverForIpc; Loading Loading @@ -91,10 +93,13 @@ public class CompanionDeviceActivity extends Activity { // The flag used to prevent double taps, that may lead to sending several requests for creating // an association to CDM. private boolean mAssociationApproved; private boolean mApproved; private boolean mCancelled; @Override public void onCreate(Bundle savedInstanceState) { if (DEBUG) Log.d(TAG, "onCreate()"); super.onCreate(savedInstanceState); getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); } Loading @@ -117,25 +122,12 @@ public class CompanionDeviceActivity extends Activity { // Start discovery services if needed. if (!mRequest.isSelfManaged()) { CompanionDeviceDiscoveryService.startForRequest(this, mRequest); TIMEOUT_OBSERVABLE.addObserver((o, arg) -> cancel(true)); } // Init UI. initUI(); } @Override protected void onStop() { super.onStop(); if (DEBUG) Log.d(TAG, "onStop(), finishing=" + isFinishing()); // TODO: handle config changes without cancelling. if (!isFinishing()) { cancel(); // will finish() } // mAdapter may be observing - need to remove it. CompanionDeviceDiscoveryService.SCAN_RESULTS_OBSERVABLE.deleteObservers(); } @Override protected void onNewIntent(Intent intent) { // Handle another incoming request (while we are not done with the original - mRequest - Loading @@ -153,6 +145,39 @@ public class CompanionDeviceActivity extends Activity { } } @Override protected void onStop() { super.onStop(); if (DEBUG) Log.d(TAG, "onStop(), finishing=" + isFinishing()); // TODO: handle config changes without cancelling. if (!isDone()) { cancel(false); // will finish() } TIMEOUT_OBSERVABLE.deleteObservers(); // mAdapter may also be observing - need to remove it. SCAN_RESULTS_OBSERVABLE.deleteObservers(); } @Override protected void onDestroy() { super.onDestroy(); if (DEBUG) Log.d(TAG, "onDestroy()"); } @Override public void onBackPressed() { if (DEBUG) Log.d(TAG, "onBackPressed()"); super.onBackPressed(); } @Override public void finish() { if (DEBUG) Log.d(TAG, "finish()", new Exception("Stack Trace Dump")); super.finish(); } private void initUI() { if (DEBUG) Log.d(TAG, "initUI(), request=" + mRequest); Loading @@ -164,10 +189,9 @@ public class CompanionDeviceActivity extends Activity { mListView = findViewById(R.id.device_list); mListView.setOnItemClickListener((av, iv, position, id) -> onListItemClick(position)); mButtonAllow = findViewById(R.id.button_allow); mButtonAllow.setOnClickListener(this::onAllowButtonClick); findViewById(R.id.button_cancel).setOnClickListener(v -> cancel()); mButtonAllow = findViewById(R.id.btn_positive); mButtonAllow.setOnClickListener(this::onPositiveButtonClick); findViewById(R.id.btn_negative).setOnClickListener(this::onNegativeButtonClick); final CharSequence appLabel = getApplicationLabel(this, mRequest.getPackageName()); if (mRequest.isSelfManaged()) { Loading @@ -179,6 +203,33 @@ public class CompanionDeviceActivity extends Activity { } } private void onAssociationApproved(@Nullable MacAddress macAddress) { if (isDone()) { if (DEBUG) Log.w(TAG, "Already done: " + (mApproved ? "Approved" : "Cancelled")); return; } mApproved = true; if (DEBUG) Log.i(TAG, "onAssociationApproved() macAddress=" + macAddress); if (!mRequest.isSelfManaged()) { requireNonNull(macAddress); CompanionDeviceDiscoveryService.stop(this); } final Bundle data = new Bundle(); data.putParcelable(EXTRA_ASSOCIATION_REQUEST, mRequest); data.putBinder(EXTRA_APPLICATION_CALLBACK, mAppCallback.asBinder()); if (macAddress != null) { data.putParcelable(EXTRA_MAC_ADDRESS, macAddress); } data.putParcelable(EXTRA_RESULT_RECEIVER, prepareResultReceiverForIpc(mOnAssociationCreatedReceiver)); mCdmServiceReceiver.send(RESULT_CODE_ASSOCIATION_APPROVED, data); } private void onAssociationCreated(@NonNull AssociationInfo association) { if (DEBUG) Log.i(TAG, "onAssociationCreated(), association=" + association); Loading @@ -186,17 +237,26 @@ public class CompanionDeviceActivity extends Activity { setResultAndFinish(association); } private void cancel() { if (DEBUG) Log.i(TAG, "cancel()"); private void cancel(boolean discoveryTimeout) { if (DEBUG) { Log.i(TAG, "cancel(), discoveryTimeout=" + discoveryTimeout, new Exception("Stack Trace Dump")); } if (isDone()) { if (DEBUG) Log.w(TAG, "Already done: " + (mApproved ? "Approved" : "Cancelled")); return; } mCancelled = true; // Stop discovery service if it was used. if (!mRequest.isSelfManaged()) { if (!mRequest.isSelfManaged() || discoveryTimeout) { CompanionDeviceDiscoveryService.stop(this); } // First send callback to the app directly... try { mAppCallback.onFailure("Cancelled."); mAppCallback.onFailure(discoveryTimeout ? "Timeout." : "Cancelled."); } catch (RemoteException ignore) { } Loading Loading @@ -297,7 +357,7 @@ public class CompanionDeviceActivity extends Activity { mSummary.setText(summary); mAdapter = new DeviceListAdapter(this); CompanionDeviceDiscoveryService.SCAN_RESULTS_OBSERVABLE.addObserver(mAdapter); SCAN_RESULTS_OBSERVABLE.addObserver(mAdapter); // TODO: hide the list and show a spinner until a first device matching device is found. mListView.setAdapter(mAdapter); Loading @@ -313,8 +373,8 @@ public class CompanionDeviceActivity extends Activity { onAssociationApproved(macAddress); } private void onAllowButtonClick(View v) { if (DEBUG) Log.d(TAG, "onAllowButtonClick()"); private void onPositiveButtonClick(View v) { if (DEBUG) Log.d(TAG, "on_Positive_ButtonClick()"); // Disable the button, to prevent more clicks. v.setEnabled(false); Loading @@ -330,28 +390,17 @@ public class CompanionDeviceActivity extends Activity { onAssociationApproved(macAddress); } private void onAssociationApproved(@Nullable MacAddress macAddress) { if (mAssociationApproved) return; mAssociationApproved = true; private void onNegativeButtonClick(View v) { if (DEBUG) Log.d(TAG, "on_Negative_ButtonClick()"); if (DEBUG) Log.i(TAG, "onAssociationApproved() macAddress=" + macAddress); // Disable the button, to prevent more clicks. v.setEnabled(false); if (!mRequest.isSelfManaged()) { requireNonNull(macAddress); CompanionDeviceDiscoveryService.stop(this); cancel(false); } final Bundle data = new Bundle(); data.putParcelable(EXTRA_ASSOCIATION_REQUEST, mRequest); data.putBinder(EXTRA_APPLICATION_CALLBACK, mAppCallback.asBinder()); if (macAddress != null) { data.putParcelable(EXTRA_MAC_ADDRESS, macAddress); } data.putParcelable(EXTRA_RESULT_RECEIVER, prepareResultReceiverForIpc(mOnAssociationCreatedReceiver)); mCdmServiceReceiver.send(RESULT_CODE_ASSOCIATION_APPROVED, data); private boolean isDone() { return mApproved || mCancelled; } private final ResultReceiver mOnAssociationCreatedReceiver = Loading
packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java +24 −3 Original line number Diff line number Diff line Loading @@ -22,6 +22,8 @@ import static com.android.internal.util.CollectionUtils.filter; import static com.android.internal.util.CollectionUtils.find; import static com.android.internal.util.CollectionUtils.map; import static java.lang.Math.max; import static java.lang.Math.min; import static java.util.Objects.requireNonNull; import android.annotation.MainThread; Loading Loading @@ -50,6 +52,7 @@ import android.net.wifi.WifiManager; import android.os.Handler; import android.os.IBinder; import android.os.Parcelable; import android.os.SystemProperties; import android.text.TextUtils; import android.util.Log; Loading @@ -64,13 +67,17 @@ public class CompanionDeviceDiscoveryService extends Service { private static final boolean DEBUG = false; private static final String TAG = CompanionDeviceDiscoveryService.class.getSimpleName(); private static final String SYS_PROP_DEBUG_TIMEOUT = "debug.cdm.discovery_timeout"; private static final long TIMEOUT_DEFAULT = 20_000L; // 20 seconds private static final long TIMEOUT_MIN = 1_000L; // 1 sec private static final long TIMEOUT_MAX = 60_000L; // 1 min private static final String ACTION_START_DISCOVERY = "com.android.companiondevicemanager.action.START_DISCOVERY"; private static final String ACTION_STOP_DISCOVERY = "com.android.companiondevicemanager.action.ACTION_STOP_DISCOVERY"; private static final String EXTRA_ASSOCIATION_REQUEST = "association_request"; private static final long SCAN_TIMEOUT = 20_000L; // 20 seconds // TODO: replace with LiveData-s? static final Observable TIMEOUT_OBSERVABLE = new MyObservable(); Loading Loading @@ -180,8 +187,7 @@ public class CompanionDeviceDiscoveryService extends Service { // Start BLE scanning (if needed) mBleScanCallback = startBleScanningIfNeeded(bleFilters, forceStartScanningAll); // Schedule a time-out. Handler.getMain().postDelayed(mTimeoutRunnable, SCAN_TIMEOUT); scheduleTimeout(); } @MainThread Loading Loading @@ -338,6 +344,21 @@ public class CompanionDeviceDiscoveryService extends Service { }); } private void scheduleTimeout() { long timeout = SystemProperties.getLong(SYS_PROP_DEBUG_TIMEOUT, -1); if (timeout <= 0) { // 0 or negative values indicate that the sysprop was never set or should be ignored. timeout = TIMEOUT_DEFAULT; } else { timeout = min(timeout, TIMEOUT_MAX); // should be <= 1 min (TIMEOUT_MAX) timeout = max(timeout, TIMEOUT_MIN); // should be >= 1 sec (TIMEOUT_MIN) } if (DEBUG) Log.d(TAG, "scheduleTimeout(), timeout=" + timeout); Handler.getMain().postDelayed(mTimeoutRunnable, timeout); } private void timeout() { if (DEBUG) Log.i(TAG, "timeout()"); stopDiscoveryAndFinish(); Loading
packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceListAdapter.java +24 −55 Original line number Diff line number Diff line Loading @@ -16,18 +16,14 @@ package com.android.companiondevicemanager; import android.annotation.ColorInt; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Color; import android.graphics.drawable.Drawable; import android.util.TypedValue; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.TextView; import java.util.List; Loading @@ -39,51 +35,12 @@ import java.util.Observer; */ class DeviceListAdapter extends BaseAdapter implements Observer { private final Context mContext; private final Resources mResources; private final Drawable mBluetoothIcon; private final Drawable mWifiIcon; private final @ColorInt int mTextColor; // List if pairs (display name, address) private List<DeviceFilterPair<?>> mDevices; DeviceListAdapter(Context context) { mContext = context; mResources = context.getResources(); mBluetoothIcon = getTintedIcon(mResources, android.R.drawable.stat_sys_data_bluetooth); mWifiIcon = getTintedIcon(mResources, com.android.internal.R.drawable.ic_wifi_signal_3); mTextColor = getColor(context, android.R.attr.colorForeground); } @Override public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) { final TextView view = convertView != null ? (TextView) convertView : newView(); bind(view, getItem(position)); return view; } private void bind(TextView textView, DeviceFilterPair<?> item) { textView.setText(item.getDisplayName()); textView.setBackgroundColor(Color.TRANSPARENT); /* textView.setCompoundDrawablesWithIntrinsicBounds( item.getDevice() instanceof android.net.wifi.ScanResult ? mWifiIcon : mBluetoothIcon, null, null, null); textView.getCompoundDrawables()[0].setTint(mTextColor); */ } private TextView newView() { final TextView textView = new TextView(mContext); textView.setTextColor(mTextColor); final int padding = 24; textView.setPadding(padding, padding, padding, padding); //textView.setCompoundDrawablePadding(padding); return textView; } @Override Loading @@ -107,17 +64,29 @@ class DeviceListAdapter extends BaseAdapter implements Observer { notifyDataSetChanged(); } private @ColorInt int getColor(Context context, int attr) { final TypedArray a = context.obtainStyledAttributes(new TypedValue().data, new int[] { attr }); final int color = a.getColor(0, 0); a.recycle(); return color; @Override public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) { final View view = convertView != null ? convertView : LayoutInflater.from(mContext).inflate(R.layout.list_item_device, parent, false); final DeviceFilterPair<?> item = getItem(position); bindView(view, item); return view; } private static Drawable getTintedIcon(Resources resources, int drawableRes) { Drawable icon = resources.getDrawable(drawableRes, null); icon.setTint(Color.DKGRAY); return icon; private void bindView(@NonNull View view, DeviceFilterPair<?> item) { final TextView textView = view.findViewById(android.R.id.text1); textView.setText(item.getDisplayName()); final ImageView iconView = view.findViewById(android.R.id.icon); // TODO(b/211417476): Set either Bluetooth or WiFi icon. iconView.setVisibility(View.GONE); // final int iconRes = isBt ? android.R.drawable.stat_sys_data_bluetooth // : com.android.internal.R.drawable.ic_wifi_signal_3; // final Drawable icon = getTintedIcon(mResources, iconRes); // iconView.setImageDrawable(icon); } }