• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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.car.settings.accounts;
18 
19 import android.car.drivingstate.CarUxRestrictions;
20 import android.content.ContentResolver;
21 import android.content.Context;
22 import android.content.SyncAdapterType;
23 import android.content.SyncInfo;
24 import android.content.SyncStatusInfo;
25 import android.content.SyncStatusObserver;
26 
27 import androidx.annotation.VisibleForTesting;
28 
29 import com.android.car.settings.R;
30 import com.android.car.settings.common.FragmentController;
31 import com.android.car.ui.preference.CarUiTwoActionIconPreference;
32 import com.android.settingslib.utils.ThreadUtils;
33 
34 import java.util.List;
35 import java.util.Set;
36 
37 /**
38  * Controller for the preference that shows information about an account, including info about
39  * failures. It also handles a secondary button for account syncing.
40  */
41 public class AccountDetailsWithSyncStatusPreferenceController extends
42         AccountDetailsBasePreferenceController {
43     private Object mStatusChangeListenerHandle;
44     private SyncStatusObserver mSyncStatusObserver =
45             which -> ThreadUtils.postOnMainThread(() -> {
46                 // The observer call may occur even if the fragment hasn't been started, so
47                 // only force an update if the fragment hasn't been stopped.
48                 if (isStarted()) {
49                     refreshUi();
50                 }
51             });
52 
AccountDetailsWithSyncStatusPreferenceController(Context context, String preferenceKey, FragmentController fragmentController, CarUxRestrictions uxRestrictions)53     public AccountDetailsWithSyncStatusPreferenceController(Context context, String preferenceKey,
54             FragmentController fragmentController, CarUxRestrictions uxRestrictions) {
55         super(context, preferenceKey, fragmentController, uxRestrictions);
56     }
57 
58     /**
59      * Registers the account update and sync status change callbacks.
60      */
61     @Override
onStartInternal()62     protected void onStartInternal() {
63         mStatusChangeListenerHandle = ContentResolver.addStatusChangeListener(
64                 ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE
65                         | ContentResolver.SYNC_OBSERVER_TYPE_STATUS
66                         | ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, mSyncStatusObserver);
67     }
68 
69     /**
70      * Unregisters the account update and sync status change callbacks.
71      */
72     @Override
onStopInternal()73     protected void onStopInternal() {
74         if (mStatusChangeListenerHandle != null) {
75             ContentResolver.removeStatusChangeListener(mStatusChangeListenerHandle);
76         }
77     }
78 
79     @Override
updateState(CarUiTwoActionIconPreference preference)80     protected void updateState(CarUiTwoActionIconPreference preference) {
81         super.updateState(preference);
82         if (isSyncFailing()) {
83             preference.setSummary(R.string.sync_is_failing);
84         } else {
85             preference.setSummary("");
86         }
87         updateSyncButton();
88     }
89 
isSyncFailing()90     private boolean isSyncFailing() {
91         int userId = getUserHandle().getIdentifier();
92         List<SyncInfo> currentSyncs = getCurrentSyncs(userId);
93         boolean syncIsFailing = false;
94 
95         Set<SyncAdapterType> syncAdapters = AccountSyncHelper.getVisibleSyncAdaptersForAccount(
96                 getContext(), getAccount(), getUserHandle());
97         for (SyncAdapterType syncAdapter : syncAdapters) {
98             String authority = syncAdapter.authority;
99 
100             SyncStatusInfo status = ContentResolver.getSyncStatusAsUser(getAccount(), authority,
101                     userId);
102             boolean syncEnabled = ContentResolver.getSyncAutomaticallyAsUser(getAccount(),
103                     authority, userId);
104             boolean activelySyncing = AccountSyncHelper.isSyncing(getAccount(), currentSyncs,
105                     authority);
106 
107             AccountSyncHelper.SyncState syncState = AccountSyncHelper.getSyncState(status,
108                     syncEnabled, activelySyncing);
109 
110             boolean syncIsPending = status != null && status.pending;
111             if (syncState == AccountSyncHelper.SyncState.FAILED && !activelySyncing
112                     && !syncIsPending) {
113                 syncIsFailing = true;
114             }
115         }
116 
117         return syncIsFailing;
118     }
119 
updateSyncButton()120     private void updateSyncButton() {
121         // Set the button to either request or cancel sync, depending on the current state
122         boolean hasActiveSyncs = !getCurrentSyncs(
123                 getUserHandle().getIdentifier()).isEmpty();
124 
125         // If there are active syncs, clicking the button with cancel them. Otherwise, clicking the
126         // button will start them.
127         getPreference().setSecondaryActionIcon(
128                 hasActiveSyncs ? R.drawable.ic_sync_cancel : R.drawable.ic_sync);
129         getPreference().setOnSecondaryActionClickListener(hasActiveSyncs
130                 ? this::cancelSyncForEnabledProviders
131                 : this::requestSyncForEnabledProviders);
132     }
133 
requestSyncForEnabledProviders()134     private void requestSyncForEnabledProviders() {
135         int userId = getUserHandle().getIdentifier();
136 
137         Set<SyncAdapterType> adapters = AccountSyncHelper.getSyncableSyncAdaptersForAccount(
138                 getAccount(), getUserHandle());
139         for (SyncAdapterType adapter : adapters) {
140             requestSync(adapter.authority, userId);
141         }
142     }
143 
cancelSyncForEnabledProviders()144     private void cancelSyncForEnabledProviders() {
145         int userId = getUserHandle().getIdentifier();
146 
147         Set<SyncAdapterType> adapters = AccountSyncHelper.getSyncableSyncAdaptersForAccount(
148                 getAccount(), getUserHandle());
149         for (SyncAdapterType adapter : adapters) {
150             cancelSync(adapter.authority, userId);
151         }
152     }
153 
154     @VisibleForTesting
getCurrentSyncs(int userId)155     List<SyncInfo> getCurrentSyncs(int userId) {
156         return ContentResolver.getCurrentSyncsAsUser(userId);
157     }
158 
159     @VisibleForTesting
requestSync(String authority, int userId)160     void requestSync(String authority, int userId) {
161         AccountSyncHelper.requestSyncIfAllowed(getAccount(), authority, userId);
162     }
163 
164     @VisibleForTesting
cancelSync(String authority, int userId)165     void cancelSync(String authority, int userId) {
166         ContentResolver.cancelSyncAsUser(getAccount(), authority, userId);
167     }
168 }
169