Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0bf57f53 authored by Eugene Susla's avatar Eugene Susla Committed by Android (Google) Code Review
Browse files

Merge "Use Activity Context for CDM ListView Adapter" into sc-dev

parents 1a621e8e 145d5a89
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -41,13 +41,13 @@
        android:supportsRtl="true">

        <service
            android:name=".DeviceDiscoveryService"
            android:name=".CompanionDeviceDiscoveryService"
            android:permission="android.permission.BIND_COMPANION_DEVICE_MANAGER_SERVICE"
            android:exported="true">
        </service>

        <activity
            android:name=".DeviceChooserActivity"
            android:name=".CompanionDeviceActivity"
            android:theme="@style/ChooserActivity"
            android:permission="android.permission.BIND_COMPANION_DEVICE_MANAGER_SERVICE"
            android:exported="true">
+118 −18
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTE

import static java.util.Objects.requireNonNull;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Activity;
import android.companion.AssociationRequest;
@@ -31,41 +32,52 @@ import android.companion.CompanionDeviceManager;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.database.DataSetObserver;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.text.Html;
import android.util.Log;
import android.util.SparseArray;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.widget.AdapterView;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;

import com.android.companiondevicemanager.DeviceDiscoveryService.DeviceFilterPair;
import com.android.companiondevicemanager.CompanionDeviceDiscoveryService.DeviceFilterPair;
import com.android.internal.util.Preconditions;

public class DeviceChooserActivity extends Activity {
public class CompanionDeviceActivity extends Activity {

    private static final boolean DEBUG = false;
    private static final String LOG_TAG = "DeviceChooserActivity";
    private static final String LOG_TAG = CompanionDeviceActivity.class.getSimpleName();

    static CompanionDeviceActivity sInstance;

    View mLoadingIndicator = null;
    ListView mDeviceListView;
    private View mPairButton;
    private View mCancelButton;

    DevicesAdapter mDevicesAdapter;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (DEBUG) Log.i(LOG_TAG, "Started with intent " + getIntent());
        Log.i(LOG_TAG, "Starting UI for " + getService().mRequest);

        if (getService().mDevicesFound.isEmpty()) {
            Log.e(LOG_TAG, "About to show UI, but no devices to show");
        }

        getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
        sInstance = this;

        String deviceProfile = getRequest().getDeviceProfile();
        String profilePrivacyDisclaimer = emptyIfNull(getRequest()
@@ -96,17 +108,14 @@ public class DeviceChooserActivity extends Activity {
                    profileName,
                    getCallingAppName()), 0));
            mDeviceListView = findViewById(R.id.device_list);
            final DeviceDiscoveryService.DevicesAdapter adapter = getService().mDevicesAdapter;
            mDeviceListView.setAdapter(adapter);
            mDeviceListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
                @Override
                public void onItemClick(AdapterView<?> adapterView, View view, int pos, long l) {
            mDevicesAdapter = new DevicesAdapter();
            mDeviceListView.setAdapter(mDevicesAdapter);
            mDeviceListView.setOnItemClickListener((adapterView, view, pos, l) -> {
                getService().mSelectedDevice =
                        (DeviceFilterPair) adapterView.getItemAtPosition(pos);
                    adapter.notifyDataSetChanged();
                }
                mDevicesAdapter.notifyDataSetChanged();
            });
            adapter.registerDataSetObserver(new DataSetObserver() {
            mDevicesAdapter.registerDataSetObserver(new DataSetObserver() {
                @Override
                public void onChanged() {
                    onSelectionUpdate();
@@ -133,6 +142,12 @@ public class DeviceChooserActivity extends Activity {
        mCancelButton.setOnClickListener(v -> cancel());
    }

    static void notifyDevicesChanged() {
        if (sInstance != null && !sInstance.isFinishing()) {
            sInstance.mDevicesAdapter.notifyDataSetChanged();
        }
    }

    private AssociationRequest getRequest() {
        return getService().mRequest;
    }
@@ -156,6 +171,7 @@ public class DeviceChooserActivity extends Activity {
    }

    private void cancel() {
        Log.i(LOG_TAG, "cancel()");
        getService().onCancel();
        setResult(RESULT_CANCELED);
        finish();
@@ -170,6 +186,14 @@ public class DeviceChooserActivity extends Activity {
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (sInstance == this) {
            sInstance = null;
        }
    }

    private CharSequence getCallingAppName() {
        try {
            final PackageManager packageManager = getPackageManager();
@@ -217,15 +241,91 @@ public class DeviceChooserActivity extends Activity {
        }
    }

    private DeviceDiscoveryService getService() {
        return DeviceDiscoveryService.sInstance;
    private CompanionDeviceDiscoveryService getService() {
        return CompanionDeviceDiscoveryService.sInstance;
    }

    protected void onDeviceConfirmed(DeviceFilterPair selectedDevice) {
        Log.i(LOG_TAG, "onDeviceConfirmed(selectedDevice = " + selectedDevice + ")");
        getService().onDeviceSelected(
                getCallingPackage(), getDeviceMacAddress(selectedDevice.device));
        setResult(RESULT_OK,
                new Intent().putExtra(CompanionDeviceManager.EXTRA_DEVICE, selectedDevice.device));
        finish();
    }

    class DevicesAdapter extends BaseAdapter {
        private final Drawable mBluetoothIcon = icon(android.R.drawable.stat_sys_data_bluetooth);
        private final Drawable mWifiIcon = icon(com.android.internal.R.drawable.ic_wifi_signal_3);

        private SparseArray<Integer> mColors = new SparseArray();

        private Drawable icon(int drawableRes) {
            Drawable icon = getResources().getDrawable(drawableRes, null);
            icon.setTint(Color.DKGRAY);
            return icon;
        }

        @Override
        public View getView(
                int position,
                @Nullable View convertView,
                @NonNull ViewGroup parent) {
            TextView view = convertView instanceof TextView
                    ? (TextView) convertView
                    : newView();
            bind(view, getItem(position));
            return view;
        }

        private void bind(TextView textView, DeviceFilterPair device) {
            textView.setText(device.getDisplayName());
            textView.setBackgroundColor(
                    device.equals(getService().mSelectedDevice)
                            ? getColor(android.R.attr.colorControlHighlight)
                            : Color.TRANSPARENT);
            textView.setCompoundDrawablesWithIntrinsicBounds(
                    device.device instanceof android.net.wifi.ScanResult
                            ? mWifiIcon
                            : mBluetoothIcon,
                    null, null, null);
            textView.getCompoundDrawables()[0].setTint(getColor(android.R.attr.colorForeground));
        }

        private TextView newView() {
            final TextView textView = new TextView(CompanionDeviceActivity.this);
            textView.setTextColor(getColor(android.R.attr.colorForeground));
            final int padding = CompanionDeviceActivity.getPadding(getResources());
            textView.setPadding(padding, padding, padding, padding);
            textView.setCompoundDrawablePadding(padding);
            return textView;
        }

        private int getColor(int colorAttr) {
            if (mColors.contains(colorAttr)) {
                return mColors.get(colorAttr);
            }
            TypedValue typedValue = new TypedValue();
            TypedArray a = obtainStyledAttributes(typedValue.data, new int[] { colorAttr });
            int result = a.getColor(0, 0);
            a.recycle();
            mColors.put(colorAttr, result);
            return result;
        }

        @Override
        public int getCount() {
            return getService().mDevicesFound.size();
        }

        @Override
        public DeviceFilterPair getItem(int position) {
            return getService().mDevicesFound.get(position);
        }

        @Override
        public long getItemId(int position) {
            return position;
        }
    }
}
+17 −100
Original line number Diff line number Diff line
@@ -50,9 +50,6 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.net.wifi.WifiManager;
import android.os.Handler;
import android.os.IBinder;
@@ -60,12 +57,6 @@ import android.os.Parcelable;
import android.os.RemoteException;
import android.text.TextUtils;
import android.util.Log;
import android.util.SparseArray;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

import com.android.internal.infra.AndroidFuture;
import com.android.internal.util.ArrayUtils;
@@ -76,14 +67,14 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

public class DeviceDiscoveryService extends Service {
public class CompanionDeviceDiscoveryService extends Service {

    private static final boolean DEBUG = false;
    private static final String LOG_TAG = "DeviceDiscoveryService";
    private static final String LOG_TAG = CompanionDeviceDiscoveryService.class.getSimpleName();

    private static final long SCAN_TIMEOUT = 20000;

    static DeviceDiscoveryService sInstance;
    static CompanionDeviceDiscoveryService sInstance;

    private BluetoothManager mBluetoothManager;
    private BluetoothAdapter mBluetoothAdapter;
@@ -102,12 +93,12 @@ public class DeviceDiscoveryService extends Service {
    AssociationRequest mRequest;
    List<DeviceFilterPair> mDevicesFound;
    DeviceFilterPair mSelectedDevice;
    DevicesAdapter mDevicesAdapter;
    IFindDeviceCallback mFindCallback;

    AndroidFuture<Association> mServiceCallback;
    boolean mIsScanning = false;
    @Nullable DeviceChooserActivity mActivity = null;
    @Nullable
    CompanionDeviceActivity mActivity = null;

    private final ICompanionDeviceDiscoveryService mBinder =
            new ICompanionDeviceDiscoveryService.Stub() {
@@ -125,7 +116,8 @@ public class DeviceDiscoveryService extends Service {
            mFindCallback = findCallback;
            mServiceCallback = serviceCallback;
            Handler.getMain().sendMessage(obtainMessage(
                    DeviceDiscoveryService::startDiscovery, DeviceDiscoveryService.this, request));
                    CompanionDeviceDiscoveryService::startDiscovery,
                    CompanionDeviceDiscoveryService.this, request));
        }
    };

@@ -151,7 +143,6 @@ public class DeviceDiscoveryService extends Service {
        mWifiManager = getSystemService(WifiManager.class);

        mDevicesFound = new ArrayList<>();
        mDevicesAdapter = new DevicesAdapter();

        sInstance = this;
    }
@@ -165,7 +156,8 @@ public class DeviceDiscoveryService extends Service {
            mWifiFilters = CollectionUtils.filter(mFilters, WifiDeviceFilter.class);
            mBluetoothFilters = CollectionUtils.filter(mFilters, BluetoothDeviceFilter.class);
            mBLEFilters = CollectionUtils.filter(mFilters, BluetoothLeDeviceFilter.class);
            mBLEScanFilters = CollectionUtils.map(mBLEFilters, BluetoothLeDeviceFilter::getScanFilter);
            mBLEScanFilters
                    = CollectionUtils.map(mBLEFilters, BluetoothLeDeviceFilter::getScanFilter);

            reset();
        } else if (DEBUG) Log.i(LOG_TAG, "startDiscovery: duplicate request: " + request);
@@ -223,7 +215,7 @@ public class DeviceDiscoveryService extends Service {
        }
        mIsScanning = true;
        Handler.getMain().sendMessageDelayed(
                obtainMessage(DeviceDiscoveryService::stopScan, this),
                obtainMessage(CompanionDeviceDiscoveryService::stopScan, this),
                SCAN_TIMEOUT);
    }

@@ -237,7 +229,7 @@ public class DeviceDiscoveryService extends Service {
        stopScan();
        mDevicesFound.clear();
        mSelectedDevice = null;
        mDevicesAdapter.notifyDataSetChanged();
        CompanionDeviceActivity.notifyDevicesChanged();
    }

    @Override
@@ -252,7 +244,7 @@ public class DeviceDiscoveryService extends Service {
        if (!mIsScanning) return;
        mIsScanning = false;

        DeviceChooserActivity activity = mActivity;
        CompanionDeviceActivity activity = mActivity;
        if (activity != null) {
            if (activity.mDeviceListView != null) {
                activity.mDeviceListView.removeFooterView(activity.mLoadingIndicator);
@@ -276,7 +268,7 @@ public class DeviceDiscoveryService extends Service {
        if (device == null) return;

        Handler.getMain().sendMessage(obtainMessage(
                DeviceDiscoveryService::onDeviceFoundMainThread, this, device));
                CompanionDeviceDiscoveryService::onDeviceFoundMainThread, this, device));
    }

    @MainThread
@@ -292,7 +284,7 @@ public class DeviceDiscoveryService extends Service {
            onReadyToShowUI();
        }
        mDevicesFound.add(device);
        mDevicesAdapter.notifyDataSetChanged();
        CompanionDeviceActivity.notifyDevicesChanged();
    }

    //TODO also, on timeout -> call onFailure
@@ -300,7 +292,7 @@ public class DeviceDiscoveryService extends Service {
        try {
            mFindCallback.onSuccess(PendingIntent.getActivity(
                    this, 0,
                    new Intent(this, DeviceChooserActivity.class),
                    new Intent(this, CompanionDeviceActivity.class),
                    PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_CANCEL_CURRENT
                            | PendingIntent.FLAG_IMMUTABLE));
        } catch (RemoteException e) {
@@ -311,13 +303,13 @@ public class DeviceDiscoveryService extends Service {
    private void onDeviceLost(@Nullable DeviceFilterPair device) {
        Log.i(LOG_TAG, "Lost device " + device.getDisplayName());
        Handler.getMain().sendMessage(obtainMessage(
                DeviceDiscoveryService::onDeviceLostMainThread, this, device));
                CompanionDeviceDiscoveryService::onDeviceLostMainThread, this, device));
    }

    @MainThread
    void onDeviceLostMainThread(@Nullable DeviceFilterPair device) {
        mDevicesFound.remove(device);
        mDevicesAdapter.notifyDataSetChanged();
        CompanionDeviceActivity.notifyDevicesChanged();
    }

    void onDeviceSelected(String callingPackage, String deviceAddress) {
@@ -331,81 +323,6 @@ public class DeviceDiscoveryService extends Service {
        mServiceCallback.cancel(true);
    }

    class DevicesAdapter extends BaseAdapter {
        private Drawable BLUETOOTH_ICON = icon(android.R.drawable.stat_sys_data_bluetooth);
        private Drawable WIFI_ICON = icon(com.android.internal.R.drawable.ic_wifi_signal_3);

        private SparseArray<Integer> mColors = new SparseArray();

        private Drawable icon(int drawableRes) {
            Drawable icon = getResources().getDrawable(drawableRes, null);
            icon.setTint(Color.DKGRAY);
            return icon;
        }

        @Override
        public View getView(
                int position,
                @Nullable View convertView,
                @NonNull ViewGroup parent) {
            TextView view = convertView instanceof TextView
                    ? (TextView) convertView
                    : newView();
            bind(view, getItem(position));
            return view;
        }

        private void bind(TextView textView, DeviceFilterPair device) {
            textView.setText(device.getDisplayName());
            textView.setBackgroundColor(
                    device.equals(mSelectedDevice)
                            ? getColor(android.R.attr.colorControlHighlight)
                            : Color.TRANSPARENT);
            textView.setCompoundDrawablesWithIntrinsicBounds(
                    device.device instanceof android.net.wifi.ScanResult
                        ? WIFI_ICON
                        : BLUETOOTH_ICON,
                    null, null, null);
            textView.getCompoundDrawables()[0].setTint(getColor(android.R.attr.colorForeground));
        }

        private TextView newView() {
            final TextView textView = new TextView(DeviceDiscoveryService.this);
            textView.setTextColor(getColor(android.R.attr.colorForeground));
            final int padding = DeviceChooserActivity.getPadding(getResources());
            textView.setPadding(padding, padding, padding, padding);
            textView.setCompoundDrawablePadding(padding);
            return textView;
        }

        private int getColor(int colorAttr) {
            if (mColors.contains(colorAttr)) {
                return mColors.get(colorAttr);
            }
            TypedValue typedValue = new TypedValue();
            TypedArray a = obtainStyledAttributes(typedValue.data, new int[] { colorAttr });
            int result = a.getColor(0, 0);
            a.recycle();
            mColors.put(colorAttr, result);
            return result;
        }

        @Override
        public int getCount() {
            return mDevicesFound.size();
        }

        @Override
        public DeviceFilterPair getItem(int position) {
            return mDevicesFound.get(position);
        }

        @Override
        public long getItemId(int position) {
            return position;
        }
    }

    /**
     * A pair of device and a filter that matched this device if any.
     *
+1 −6
Original line number Diff line number Diff line
@@ -142,18 +142,13 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Function;

//TODO onStop schedule unbind in 5 seconds
//TODO make sure APIs are only callable from currently focused app
//TODO schedule stopScan on activity destroy(except if configuration change)
//TODO on associate called again after configuration change -> replace old callback with new
//TODO avoid leaking calling activity in IFindDeviceCallback (see PrintManager#print for example)
/** @hide */
@SuppressLint("LongLogTag")
public class CompanionDeviceManagerService extends SystemService implements Binder.DeathRecipient {

    private static final ComponentName SERVICE_TO_BIND_TO = ComponentName.createRelative(
            CompanionDeviceManager.COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME,
            ".DeviceDiscoveryService");
            ".CompanionDeviceDiscoveryService");

    private static final long DEVICE_DISAPPEARED_TIMEOUT_MS = 10 * 1000;
    private static final long DEVICE_DISAPPEARED_UNBIND_TIMEOUT_MS = 10 * 60 * 1000;