1 /*
2  * Copyright (C) 2018 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.accounts.Account;
20 import android.content.ContentResolver;
21 import android.content.Context;
22 import android.content.SyncAdapterType;
23 import android.content.SyncStatusObserver;
24 import android.os.Bundle;
25 import android.os.UserHandle;
26 
27 import androidx.annotation.XmlRes;
28 
29 import com.android.car.settings.R;
30 import com.android.car.settings.common.SettingsFragment;
31 import com.android.car.ui.toolbar.MenuItem;
32 import com.android.settingslib.utils.ThreadUtils;
33 
34 import java.util.Collections;
35 import java.util.List;
36 import java.util.Set;
37 
38 /**
39  * Shows details for syncing an account.
40  */
41 public class AccountSyncDetailsFragment extends SettingsFragment {
42     private static final String EXTRA_ACCOUNT = "extra_account";
43     private static final String EXTRA_USER_HANDLE = "extra_user_handle";
44     private boolean mIsStarted = false;
45     private Object mStatusChangeListenerHandle;
46     private MenuItem mSyncButton;
47     private SyncStatusObserver mSyncStatusObserver =
48             which -> ThreadUtils.postOnMainThread(() -> {
49                 // The observer call may occur even if the fragment hasn't been started, so
50                 // only force an update if the fragment hasn't been stopped.
51                 if (mIsStarted) {
52                     updateSyncButton();
53                 }
54             });
55 
56     /**
57      * Creates a new AccountSyncDetailsFragment.
58      *
59      * <p>Passes the provided account and user handle to the fragment via fragment arguments.
60      */
newInstance(Account account, UserHandle userHandle)61     public static AccountSyncDetailsFragment newInstance(Account account, UserHandle userHandle) {
62         AccountSyncDetailsFragment accountSyncDetailsFragment = new AccountSyncDetailsFragment();
63         Bundle bundle = new Bundle();
64         bundle.putParcelable(EXTRA_ACCOUNT, account);
65         bundle.putParcelable(EXTRA_USER_HANDLE, userHandle);
66         accountSyncDetailsFragment.setArguments(bundle);
67         return accountSyncDetailsFragment;
68     }
69 
70     @Override
71     @XmlRes
getPreferenceScreenResId()72     protected int getPreferenceScreenResId() {
73         return R.xml.account_sync_details_fragment;
74     }
75 
76     @Override
getToolbarMenuItems()77     public List<MenuItem> getToolbarMenuItems() {
78         return Collections.singletonList(mSyncButton);
79     }
80 
81     @Override
onCreate(Bundle savedInstanceState)82     public void onCreate(Bundle savedInstanceState) {
83         super.onCreate(savedInstanceState);
84 
85         mSyncButton = new MenuItem.Builder(getContext()).build();
86         updateSyncButton();
87     }
88 
89     @Override
onAttach(Context context)90     public void onAttach(Context context) {
91         super.onAttach(context);
92 
93         Account account = getArguments().getParcelable(EXTRA_ACCOUNT);
94         UserHandle userHandle = getArguments().getParcelable(EXTRA_USER_HANDLE);
95 
96         use(AccountDetailsWithSyncStatusPreferenceController.class,
97                 R.string.pk_account_details_with_sync)
98                 .setAccount(account);
99         use(AccountDetailsWithSyncStatusPreferenceController.class,
100                 R.string.pk_account_details_with_sync)
101                 .setUserHandle(userHandle);
102 
103         use(AccountSyncDetailsPreferenceController.class, R.string.pk_account_sync_details)
104                 .setAccount(account);
105         use(AccountSyncDetailsPreferenceController.class, R.string.pk_account_sync_details)
106                 .setUserHandle(userHandle);
107     }
108 
109     @Override
onActivityCreated(Bundle savedInstanceState)110     public void onActivityCreated(Bundle savedInstanceState) {
111         super.onActivityCreated(savedInstanceState);
112         updateSyncButton();
113     }
114 
115     @Override
onStart()116     public void onStart() {
117         super.onStart();
118         mIsStarted = true;
119         mStatusChangeListenerHandle = ContentResolver.addStatusChangeListener(
120                 ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE
121                         | ContentResolver.SYNC_OBSERVER_TYPE_STATUS
122                         | ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, mSyncStatusObserver);
123     }
124 
125     @Override
onStop()126     public void onStop() {
127         super.onStop();
128         mIsStarted = false;
129         if (mStatusChangeListenerHandle != null) {
130             ContentResolver.removeStatusChangeListener(mStatusChangeListenerHandle);
131         }
132     }
133 
updateSyncButton()134     private void updateSyncButton() {
135         // Set the button to either request or cancel sync, depending on the current state
136         UserHandle userHandle = getArguments().getParcelable(EXTRA_USER_HANDLE);
137         boolean hasActiveSyncs = !ContentResolver.getCurrentSyncsAsUser(
138                 userHandle.getIdentifier()).isEmpty();
139 
140         // If there are active syncs, clicking the button with cancel them. Otherwise, clicking the
141         // button will start them.
142         mSyncButton.setTitle(
143                 hasActiveSyncs ? R.string.sync_button_sync_cancel : R.string.sync_button_sync_now);
144         mSyncButton.setOnClickListener(hasActiveSyncs
145                 ? i -> cancelSyncForEnabledProviders()
146                 : i -> requestSyncForEnabledProviders());
147     }
148 
requestSyncForEnabledProviders()149     private void requestSyncForEnabledProviders() {
150         Account account = getArguments().getParcelable(EXTRA_ACCOUNT);
151         UserHandle userHandle = getArguments().getParcelable(EXTRA_USER_HANDLE);
152         int userId = userHandle.getIdentifier();
153 
154         Set<SyncAdapterType> adapters = AccountSyncHelper.getSyncableSyncAdaptersForAccount(account,
155                 userHandle);
156         for (SyncAdapterType adapter : adapters) {
157             AccountSyncHelper.requestSyncIfAllowed(account, adapter.authority, userId);
158         }
159     }
160 
cancelSyncForEnabledProviders()161     private void cancelSyncForEnabledProviders() {
162         Account account = getArguments().getParcelable(EXTRA_ACCOUNT);
163         UserHandle userHandle = getArguments().getParcelable(EXTRA_USER_HANDLE);
164         int userId = userHandle.getIdentifier();
165 
166         Set<SyncAdapterType> adapters = AccountSyncHelper.getSyncableSyncAdaptersForAccount(account,
167                 userHandle);
168         for (SyncAdapterType adapter : adapters) {
169             ContentResolver.cancelSyncAsUser(account, adapter.authority, userId);
170         }
171     }
172 }
173