diff --git a/res/layout/slice_preference_layout.xml b/res/layout/slice_preference_layout.xml new file mode 100644 index 00000000000..4cea9c04678 --- /dev/null +++ b/res/layout/slice_preference_layout.xml @@ -0,0 +1,29 @@ + + + + + + diff --git a/res/values/attrs.xml b/res/values/attrs.xml index 86763b77169..383506dbb1d 100644 --- a/res/values/attrs.xml +++ b/res/values/attrs.xml @@ -52,6 +52,8 @@ + + diff --git a/res/values/styles.xml b/res/values/styles.xml index 48cd8769ab1..15ec46d01b4 100644 --- a/res/values/styles.xml +++ b/res/values/styles.xml @@ -489,4 +489,8 @@ false + + diff --git a/res/values/styles_preference.xml b/res/values/styles_preference.xml index 0f7ecabea18..f25289df748 100644 --- a/res/values/styles_preference.xml +++ b/res/values/styles_preference.xml @@ -21,6 +21,7 @@ @@ -34,6 +35,10 @@ @layout/apn_preference_layout + + diff --git a/res/values/themes.xml b/res/values/themes.xml index 390be584e21..e06c362708d 100644 --- a/res/values/themes.xml +++ b/res/values/themes.xml @@ -58,6 +58,9 @@ @*android:color/primary_device_default_settings_light @android:color/white + + + @style/Widget.SliceView.Settings diff --git a/res/xml/bluetooth_device_details_fragment.xml b/res/xml/bluetooth_device_details_fragment.xml index 40ce93db152..90895f258fe 100644 --- a/res/xml/bluetooth_device_details_fragment.xml +++ b/res/xml/bluetooth_device_details_fragment.xml @@ -26,7 +26,14 @@ settings:allowDividerBelow="true"/> + android:key="action_buttons" + settings:allowDividerBelow="true"/> + + diff --git a/src/com/android/settings/DisplaySettings.java b/src/com/android/settings/DisplaySettings.java index 6557aee26cb..fdd7f2e46b3 100644 --- a/src/com/android/settings/DisplaySettings.java +++ b/src/com/android/settings/DisplaySettings.java @@ -32,7 +32,6 @@ import com.android.settings.display.TapToWakePreferenceController; import com.android.settings.display.ThemePreferenceController; import com.android.settings.display.TimeoutPreferenceController; import com.android.settings.display.VrDisplayPreferenceController; -import com.android.settings.display.WallpaperPreferenceController; import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.search.Indexable; import com.android.settingslib.core.AbstractPreferenceController; diff --git a/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java index df321118c1e..a143d787872 100644 --- a/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java +++ b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java @@ -19,14 +19,19 @@ package com.android.settings.bluetooth; import static android.os.UserManager.DISALLOW_CONFIG_BLUETOOTH; import android.bluetooth.BluetoothDevice; +import android.content.ContentResolver; import android.content.Context; +import android.net.Uri; import android.os.Bundle; +import android.util.FeatureFlagUtils; import androidx.annotation.VisibleForTesting; import com.android.internal.logging.nano.MetricsProto; import com.android.settings.R; +import com.android.settings.core.FeatureFlags; import com.android.settings.dashboard.RestrictedDashboardFragment; +import com.android.settings.slices.SlicePreferenceController; import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.core.AbstractPreferenceController; @@ -98,6 +103,15 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment mManager = getLocalBluetoothManager(context); mCachedDevice = getCachedDevice(mDeviceAddress); super.onAttach(context); + + if (FeatureFlagUtils.isEnabled(context, FeatureFlags.SLICE_INJECTION)) { + //TODO(b/120803703): update it to get data from feature provider + use(SlicePreferenceController.class).setSliceUri(new Uri.Builder() + .scheme(ContentResolver.SCHEME_CONTENT) + .authority("com.google.android.apps.wearables.maestro.companion") + .appendPath("setting_slice") + .build()); + } } @Override diff --git a/src/com/android/settings/slices/SlicePreference.java b/src/com/android/settings/slices/SlicePreference.java new file mode 100644 index 00000000000..98719f7de8c --- /dev/null +++ b/src/com/android/settings/slices/SlicePreference.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2018 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.settings.slices; + +import android.content.Context; +import android.util.AttributeSet; + +import androidx.slice.Slice; +import androidx.slice.widget.SliceView; + +import com.android.settings.R; +import com.android.settingslib.widget.LayoutPreference; + +/** + * Preference for {@link SliceView} + */ +public class SlicePreference extends LayoutPreference { + private SliceView mSliceView; + + public SlicePreference(Context context, AttributeSet attrs) { + super(context, attrs, R.attr.slicePreferenceStyle); + mSliceView = findViewById(R.id.slice_view); + } + + public SlicePreference(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, R.attr.slicePreferenceStyle); + mSliceView = findViewById(R.id.slice_view); + } + + public void onSliceUpdated(Slice slice) { + mSliceView.onChanged(slice); + notifyChanged(); + } +} diff --git a/src/com/android/settings/slices/SlicePreferenceController.java b/src/com/android/settings/slices/SlicePreferenceController.java new file mode 100644 index 00000000000..8c751c8f609 --- /dev/null +++ b/src/com/android/settings/slices/SlicePreferenceController.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2018 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.settings.slices; + +import android.content.Context; +import android.net.Uri; + +import androidx.annotation.VisibleForTesting; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.Observer; +import androidx.preference.PreferenceScreen; +import androidx.slice.Slice; +import androidx.slice.widget.SliceLiveData; +import androidx.slice.widget.SliceView; + +import com.android.settings.core.BasePreferenceController; +import com.android.settingslib.core.lifecycle.LifecycleObserver; +import com.android.settingslib.core.lifecycle.events.OnStart; +import com.android.settingslib.core.lifecycle.events.OnStop; + +/** + * Default {@link BasePreferenceController} for {@link SliceView}. It will take {@link Uri} for + * Slice and display what's inside this {@link Uri} + */ +public class SlicePreferenceController extends BasePreferenceController implements + LifecycleObserver, OnStart, OnStop, Observer { + @VisibleForTesting + LiveData mLiveData; + private SlicePreference mSlicePreference; + private Uri mUri; + + public SlicePreferenceController(Context context, String preferenceKey) { + super(context, preferenceKey); + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + + mSlicePreference = (SlicePreference) screen.findPreference( + getPreferenceKey()); + } + + @Override + public int getAvailabilityStatus() { + return mUri != null ? AVAILABLE : UNSUPPORTED_ON_DEVICE; + } + + public void setSliceUri(Uri uri) { + mUri = uri; + mLiveData = SliceLiveData.fromUri(mContext, mUri); + + //TODO(b/120803703): figure out why we need to remove observer first + mLiveData.removeObserver(this); + } + + @Override + public void onStart() { + if (mLiveData != null) { + mLiveData.observeForever(this); + } + } + + @Override + public void onStop() { + if (mLiveData != null) { + mLiveData.removeObserver(this); + } + } + + @Override + public void onChanged(Slice slice) { + mSlicePreference.onSliceUpdated(slice); + } +} diff --git a/tests/robotests/src/com/android/settings/slices/SlicePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/slices/SlicePreferenceControllerTest.java new file mode 100644 index 00000000000..364fb60947b --- /dev/null +++ b/tests/robotests/src/com/android/settings/slices/SlicePreferenceControllerTest.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2018 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.settings.slices; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +import android.content.Context; +import android.net.Uri; + +import androidx.lifecycle.LiveData; +import androidx.slice.Slice; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; + +@RunWith(RobolectricTestRunner.class) +public class SlicePreferenceControllerTest { + private static final String KEY = "slice_preference_key"; + + @Mock + private LiveData mLiveData; + private Context mContext; + private SlicePreferenceController mController; + private Uri mUri; + + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mContext = spy(RuntimeEnvironment.application); + mController = new SlicePreferenceController(mContext, KEY); + mController.mLiveData = mLiveData; + mUri = Uri.EMPTY; + } + + @Test + public void isAvailable_uriNull_returnFalse() { + assertThat(mController.isAvailable()).isFalse(); + } + + @Test + public void isAvailable_uriNotNull_returnTrue() { + mController.setSliceUri(mUri); + assertThat(mController.isAvailable()).isTrue(); + } + + @Test + public void onStart_registerObserver() { + mController.onStart(); + verify(mLiveData).observeForever(mController); + } + + @Test + public void onStop_unregisterObserver() { + mController.onStop(); + verify(mLiveData).removeObserver(mController); + } +} \ No newline at end of file