Add skeleton code to lively update preference visibility.

Bug: 174691340

Test: Manual

Change-Id: Ib6bd0428854c474038df71676af71011fcab5890
diff --git a/Settings/res/values/attrs.xml b/Settings/res/values/attrs.xml
index cef341e..f882834 100644
--- a/Settings/res/values/attrs.xml
+++ b/Settings/res/values/attrs.xml
@@ -143,4 +143,7 @@
         <attr name="pickerType" format="string" />
     </declare-styleable>
 
+    <declare-styleable name="TsPreference">
+        <attr name="updatableFromGoogleSettings" format="boolean" />
+    </declare-styleable>
 </resources>
diff --git a/Settings/src/com/android/tv/settings/SettingsPreferenceFragment.java b/Settings/src/com/android/tv/settings/SettingsPreferenceFragment.java
index b47c4ac..d289bc1 100644
--- a/Settings/src/com/android/tv/settings/SettingsPreferenceFragment.java
+++ b/Settings/src/com/android/tv/settings/SettingsPreferenceFragment.java
@@ -41,6 +41,9 @@
 import androidx.annotation.NonNull;
 import androidx.leanback.preference.LeanbackPreferenceFragmentCompat;
 import androidx.lifecycle.LifecycleOwner;
+import androidx.lifecycle.ViewModelProvider;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceGroup;
 import androidx.preference.PreferenceGroupAdapter;
 import androidx.preference.PreferenceScreen;
 import androidx.preference.PreferenceViewHolder;
@@ -51,6 +54,9 @@
 import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin;
 import com.android.settingslib.core.lifecycle.Lifecycle;
 import com.android.tv.settings.overlay.FlavorUtils;
+import com.android.tv.settings.util.SettingsPreferenceUtil;
+import com.android.tv.settings.widget.SettingsViewModel;
+import com.android.tv.settings.widget.TsPreference;
 import com.android.tv.twopanelsettings.TwoPanelSettingsFragment;
 
 /**
@@ -126,6 +132,26 @@
             }
             removeAnimationClipping(view);
         }
+        SettingsViewModel settingsViewModel = new ViewModelProvider(this.getActivity(),
+                ViewModelProvider.AndroidViewModelFactory.getInstance(
+                        this.getActivity().getApplication())).get(SettingsViewModel.class);
+        iteratePreferenceAndSetObserver(settingsViewModel, getPreferenceScreen());
+    }
+
+    private void iteratePreferenceAndSetObserver(SettingsViewModel viewModel,
+            PreferenceGroup preferenceGroup) {
+        for (int i = 0; i < preferenceGroup.getPreferenceCount(); i++) {
+            Preference pref = preferenceGroup.getPreference(i);
+            if (pref instanceof TsPreference
+                    && ((TsPreference) pref).updatableFromGoogleSettings()) {
+                viewModel.getVisibilityLiveData(
+                        SettingsPreferenceUtil.getCompoundKey(this, pref))
+                        .observe(getViewLifecycleOwner(), (Boolean b) -> pref.setVisible(b));
+            }
+            if (pref instanceof PreferenceGroup) {
+                iteratePreferenceAndSetObserver(viewModel, (PreferenceGroup) pref);
+            }
+        }
     }
 
     protected void removeAnimationClipping(View v) {
diff --git a/Settings/src/com/android/tv/settings/util/SettingsPreferenceUtil.java b/Settings/src/com/android/tv/settings/util/SettingsPreferenceUtil.java
new file mode 100644
index 0000000..b87cc7b
--- /dev/null
+++ b/Settings/src/com/android/tv/settings/util/SettingsPreferenceUtil.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tv.settings.util;
+
+import android.view.View;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.leanback.preference.LeanbackPreferenceFragmentCompat;
+import androidx.leanback.preference.R;
+import androidx.preference.Preference;
+
+public class SettingsPreferenceUtil {
+    private static final String DELIMITER = ":";
+
+    /**
+     * Return the compound key of the preference. Format: Title:Key
+     *
+     * @param fragment fragment which preference belongs to.
+     * @return compound key of the preference
+     */
+    public static String getCompoundKey(@NonNull LeanbackPreferenceFragmentCompat fragment,
+            @NonNull Preference preference) {
+        String title = "";
+        View fragmentView = fragment.getView();
+        if (fragmentView != null && fragmentView.findViewById(R.id.decor_title) != null) {
+            title = ((TextView) (fragmentView.findViewById(R.id.decor_title))).getText().toString();
+        }
+        return title + DELIMITER + preference.getKey();
+    }
+}
diff --git a/Settings/src/com/android/tv/settings/widget/PreferenceContentProviderLiveData.java b/Settings/src/com/android/tv/settings/widget/PreferenceContentProviderLiveData.java
new file mode 100644
index 0000000..ae92394
--- /dev/null
+++ b/Settings/src/com/android/tv/settings/widget/PreferenceContentProviderLiveData.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tv.settings.widget;
+
+import android.content.Context;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.net.Uri;
+
+import androidx.lifecycle.MutableLiveData;
+
+import com.android.settingslib.utils.ThreadUtils;
+
+public class PreferenceContentProviderLiveData extends MutableLiveData<Boolean> {
+    private static final String AUTHORITY = "com.google.android.apps.tv.settings.contentprovider";
+    private static final String PATH_PREFERENCE = "preference";
+    private static final String PREFERENCE_DATA_PROVIDER_PATH =
+            "content://" + AUTHORITY + "/" + PATH_PREFERENCE;
+
+    private final ContentObserver mContentObserver = new ContentObserver(null) {
+        @Override
+        public void onChange(boolean selfChange) {
+            postGetVisibilityStatus();
+        }
+    };
+
+    private final Context mContext;
+    private final Uri mUri;
+    private final String mKey;
+    private boolean mRegistered = false;
+    private static final String VISIBLE = "visible";
+
+    public PreferenceContentProviderLiveData(String key, Context context) {
+        mKey = key;
+        mUri = Uri.parse(PREFERENCE_DATA_PROVIDER_PATH + "/" + key);
+        mContext = context;
+    }
+
+    @Override
+    protected void onActive() {
+        postGetVisibilityStatus();
+        if (!mRegistered) {
+            mContext.getContentResolver().registerContentObserver(mUri, false,
+                    mContentObserver);
+            mRegistered = true;
+        }
+    }
+
+    @Override
+    protected void onInactive() {
+        mContext.getContentResolver().unregisterContentObserver(mContentObserver);
+        mRegistered = false;
+    }
+
+    public String getKey() {
+        return mKey;
+    }
+
+    private void postGetVisibilityStatus() {
+        ThreadUtils.postOnBackgroundThread(() -> {
+            postValue(Boolean.valueOf(getVisibilityStatus()));
+        });
+    }
+
+    private boolean getVisibilityStatus() {
+        try {
+            Cursor cursor = mContext.getContentResolver().query(mUri, null, null, null);
+            if (cursor != null && cursor.getCount() != 0) {
+                cursor.moveToFirst();
+                int visible = cursor.getInt(cursor.getColumnIndex(VISIBLE));
+                return visible == 1;
+            }
+        } catch (IllegalArgumentException | NullPointerException e) {
+            return true;
+        }
+        return true;
+    }
+}
diff --git a/Settings/src/com/android/tv/settings/widget/SettingsViewModel.java b/Settings/src/com/android/tv/settings/widget/SettingsViewModel.java
new file mode 100644
index 0000000..162d6c3
--- /dev/null
+++ b/Settings/src/com/android/tv/settings/widget/SettingsViewModel.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tv.settings.widget;
+
+import android.app.Application;
+import android.util.ArrayMap;
+
+import androidx.annotation.NonNull;
+import androidx.lifecycle.AndroidViewModel;
+
+import java.util.Map;
+
+public class SettingsViewModel extends AndroidViewModel {
+    private final Map<String, PreferenceContentProviderLiveData> mPreferenceVisibilityLiveDataMap =
+            new ArrayMap<>();
+
+    public SettingsViewModel(@NonNull Application application) {
+        super(application);
+    }
+
+    public PreferenceContentProviderLiveData getVisibilityLiveData(String compoundKey) {
+        if (!mPreferenceVisibilityLiveDataMap.containsKey(compoundKey)) {
+            init(compoundKey);
+        }
+        return mPreferenceVisibilityLiveDataMap.get(compoundKey);
+    }
+
+    public void init(String compoundKey) {
+        mPreferenceVisibilityLiveDataMap.put(compoundKey,
+                new PreferenceContentProviderLiveData(compoundKey, getApplication()));
+    }
+}
diff --git a/Settings/src/com/android/tv/settings/widget/TsPreference.java b/Settings/src/com/android/tv/settings/widget/TsPreference.java
new file mode 100644
index 0000000..4737909
--- /dev/null
+++ b/Settings/src/com/android/tv/settings/widget/TsPreference.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.tv.settings.widget;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+
+import androidx.preference.Preference;
+
+import com.android.tv.settings.R;
+
+/** Augmented preference for TvSettings **/
+public class TsPreference extends Preference {
+    private boolean mUpdatableFromGoogleSettings;
+
+    public TsPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        initStyleAttributes(attrs);
+    }
+
+    public TsPreference(Context context) {
+        super(context);
+        initStyleAttributes(null);
+    }
+
+
+    private void initStyleAttributes(AttributeSet attrs) {
+        if (attrs != null) {
+            final TypedArray a = getContext().obtainStyledAttributes(
+                    attrs, R.styleable.TsPreference);
+            for (int i = a.getIndexCount() - 1; i >= 0; i--) {
+                int attr = a.getIndex(i);
+                if (attr == R.styleable.TsPreference_updatableFromGoogleSettings) {
+                    mUpdatableFromGoogleSettings = a.getBoolean(attr, false);
+                    break;
+                }
+            }
+        }
+    }
+
+    public void setUpdatableFromGoogleSettings(boolean updatableFromGoogleSettings) {
+        mUpdatableFromGoogleSettings = updatableFromGoogleSettings;
+    }
+
+    public boolean updatableFromGoogleSettings() {
+        return mUpdatableFromGoogleSettings;
+    }
+}