1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.settings.bluetooth;
18 
19 import android.companion.AssociationInfo;
20 import android.companion.CompanionDeviceManager;
21 import android.companion.Flags;
22 import android.companion.datatransfer.PermissionSyncRequest;
23 import android.content.Context;
24 
25 import androidx.preference.Preference;
26 import androidx.preference.PreferenceCategory;
27 import androidx.preference.PreferenceFragmentCompat;
28 import androidx.preference.PreferenceScreen;
29 import androidx.preference.SwitchPreferenceCompat;
30 import androidx.preference.TwoStatePreference;
31 
32 import com.android.internal.annotations.VisibleForTesting;
33 import com.android.settings.R;
34 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
35 import com.android.settingslib.core.lifecycle.Lifecycle;
36 
37 import com.google.common.base.Objects;
38 
39 import java.util.Comparator;
40 
41 /**
42  * The controller of the CDM data sync in the bluetooth detail settings.
43  */
44 public class BluetoothDetailsDataSyncController extends BluetoothDetailsController
45         implements Preference.OnPreferenceClickListener {
46 
47     private static final int DUMMY_ASSOCIATION_ID = -1;
48     private static final String TAG = "BTDataSyncController";
49     private static final String KEY_DATA_SYNC_GROUP = "data_sync_group";
50     private static final String KEY_PERM_SYNC = "perm_sync";
51 
52     @VisibleForTesting
53     PreferenceCategory mPreferenceCategory;
54     @VisibleForTesting
55     int mAssociationId = DUMMY_ASSOCIATION_ID;
56 
57     private CachedBluetoothDevice mCachedDevice;
58     private CompanionDeviceManager mCompanionDeviceManager;
59 
BluetoothDetailsDataSyncController(Context context, PreferenceFragmentCompat fragment, CachedBluetoothDevice device, Lifecycle lifecycle)60     public BluetoothDetailsDataSyncController(Context context,
61             PreferenceFragmentCompat fragment,
62             CachedBluetoothDevice device,
63             Lifecycle lifecycle) {
64         super(context, fragment, device, lifecycle);
65         mCachedDevice = device;
66         mCompanionDeviceManager = context.getSystemService(CompanionDeviceManager.class);
67 
68         mCompanionDeviceManager.getAllAssociations().stream().filter(
69                 a -> a.getDeviceMacAddress() != null).filter(
70                 a -> Objects.equal(mCachedDevice.getAddress(),
71                         a.getDeviceMacAddress().toString().toUpperCase())).max(
72                 Comparator.comparingLong(AssociationInfo::getTimeApprovedMs)).ifPresent(
73                 a -> mAssociationId = a.getId());
74     }
75 
76     @Override
isAvailable()77     public boolean isAvailable() {
78         if (!Flags.ongoingPermSync()) {
79             return false;
80         }
81         if (mAssociationId == DUMMY_ASSOCIATION_ID) {
82             return false;
83         }
84         return true;
85     }
86 
87     @Override
onPreferenceClick(Preference preference)88     public boolean onPreferenceClick(Preference preference) {
89         TwoStatePreference switchPreference = (TwoStatePreference) preference;
90         String key = switchPreference.getKey();
91         if (key.equals(KEY_PERM_SYNC)) {
92             if (switchPreference.isChecked()) {
93                 mCompanionDeviceManager.enablePermissionsSync(mAssociationId);
94             } else {
95                 mCompanionDeviceManager.disablePermissionsSync(mAssociationId);
96             }
97         }
98         return false;
99     }
100 
101     @Override
getPreferenceKey()102     public String getPreferenceKey() {
103         return KEY_DATA_SYNC_GROUP;
104     }
105 
106     @Override
init(PreferenceScreen screen)107     protected void init(PreferenceScreen screen) {
108         mPreferenceCategory = screen.findPreference(getPreferenceKey());
109         refresh();
110     }
111 
112     @Override
refresh()113     protected void refresh() {
114         TwoStatePreference permSyncPref = mPreferenceCategory.findPreference(KEY_PERM_SYNC);
115         if (permSyncPref == null) {
116             permSyncPref = createPermSyncPreference(mPreferenceCategory.getContext());
117             mPreferenceCategory.addPreference(permSyncPref);
118         }
119 
120         if (mAssociationId == DUMMY_ASSOCIATION_ID) {
121             permSyncPref.setVisible(false);
122             return;
123         }
124 
125         boolean visible = false;
126         boolean checked = false;
127         PermissionSyncRequest request = null;
128         try {
129             request = mCompanionDeviceManager.getPermissionSyncRequest(mAssociationId);
130         } catch (IllegalArgumentException e) {
131             // no-op, keep request null.
132         }
133         if (request != null) {
134             visible = true;
135             if (request.isUserConsented()) {
136                 checked = true;
137             }
138         }
139         permSyncPref.setVisible(visible);
140         permSyncPref.setChecked(checked);
141     }
142 
143     @VisibleForTesting
createPermSyncPreference(Context context)144     TwoStatePreference createPermSyncPreference(Context context) {
145         TwoStatePreference pref = new SwitchPreferenceCompat(context);
146         pref.setKey(KEY_PERM_SYNC);
147         pref.setTitle(context.getString(R.string.bluetooth_details_permissions_sync_title));
148         pref.setSummary(context.getString(R.string.bluetooth_details_permissions_sync_summary));
149         pref.setOnPreferenceClickListener(this);
150         return pref;
151     }
152 }
153