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.preference.Preference;
28 
29 import com.android.car.settings.R;
30 import com.android.car.settings.common.FragmentController;
31 import com.android.settingslib.utils.ThreadUtils;
32 
33 import java.util.List;
34 import java.util.Set;
35 
36 /**
37  * Controller for the preference that shows information about an account, including info about
38  * failures.
39  */
40 public class AccountDetailsWithSyncStatusPreferenceController extends
41         AccountDetailsPreferenceController {
42     private boolean mIsStarted = false;
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 (mIsStarted) {
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     /**
60      * Registers the account update and sync status change callbacks.
61      */
62     @Override
onStartInternal()63     protected void onStartInternal() {
64         mIsStarted = true;
65         mStatusChangeListenerHandle = ContentResolver.addStatusChangeListener(
66                 ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE
67                         | ContentResolver.SYNC_OBSERVER_TYPE_STATUS
68                         | ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, mSyncStatusObserver);
69     }
70 
71     /**
72      * Unregisters the account update and sync status change callbacks.
73      */
74     @Override
onStopInternal()75     protected void onStopInternal() {
76         mIsStarted = false;
77         if (mStatusChangeListenerHandle != null) {
78             ContentResolver.removeStatusChangeListener(mStatusChangeListenerHandle);
79         }
80     }
81 
82     @Override
updateState(Preference preference)83     protected void updateState(Preference preference) {
84         super.updateState(preference);
85         if (isSyncFailing()) {
86             preference.setSummary(R.string.sync_is_failing);
87         } else {
88             preference.setSummary("");
89         }
90     }
91 
isSyncFailing()92     private boolean isSyncFailing() {
93         int userId = getUserHandle().getIdentifier();
94         List<SyncInfo> currentSyncs = ContentResolver.getCurrentSyncsAsUser(userId);
95         boolean syncIsFailing = false;
96 
97         Set<SyncAdapterType> syncAdapters = AccountSyncHelper.getVisibleSyncAdaptersForAccount(
98                 getContext(), getAccount(), getUserHandle());
99         for (SyncAdapterType syncAdapter : syncAdapters) {
100             String authority = syncAdapter.authority;
101 
102             SyncStatusInfo status = ContentResolver.getSyncStatusAsUser(getAccount(), authority,
103                     userId);
104             boolean syncEnabled = ContentResolver.getSyncAutomaticallyAsUser(getAccount(),
105                     authority, userId);
106             boolean activelySyncing = AccountSyncHelper.isSyncing(getAccount(), currentSyncs,
107                     authority);
108 
109             AccountSyncHelper.SyncState syncState = AccountSyncHelper.getSyncState(status,
110                     syncEnabled, activelySyncing);
111 
112             boolean syncIsPending = status != null && status.pending;
113             if (syncState == AccountSyncHelper.SyncState.FAILED && !activelySyncing
114                     && !syncIsPending) {
115                 syncIsFailing = true;
116             }
117         }
118 
119         return syncIsFailing;
120     }
121 }
122