diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 2d2f5843cf3..b69cac73ad3 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -2959,6 +2959,17 @@
android:value="true" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/panel_layout.xml b/res/layout/panel_layout.xml
new file mode 100644
index 00000000000..cbdd53fce50
--- /dev/null
+++ b/res/layout/panel_layout.xml
@@ -0,0 +1,26 @@
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/settings_panel.xml b/res/layout/settings_panel.xml
new file mode 100644
index 00000000000..aec898c99c6
--- /dev/null
+++ b/res/layout/settings_panel.xml
@@ -0,0 +1,19 @@
+
+
+
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index eb7cc2becf6..bec62e9ad57 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -10299,8 +10299,8 @@
Unavailable when connected to %1$s
-
- Medical info, emergency contacts
+
+ Medical info, emergency contacts
See more
@@ -10322,6 +10322,12 @@
No connected devices
+
+ Settings Panel
+
+
+ Internet Connectivity
+
Force desktop mode
diff --git a/res/values/themes.xml b/res/values/themes.xml
index b280482de6f..873216d5ae9 100644
--- a/res/values/themes.xml
+++ b/res/values/themes.xml
@@ -200,4 +200,8 @@
- ?android:attr/colorBackground
+
+
diff --git a/res/xml/network_and_internet.xml b/res/xml/network_and_internet.xml
index 8e36e919220..c21886965fa 100644
--- a/res/xml/network_and_internet.xml
+++ b/res/xml/network_and_internet.xml
@@ -64,7 +64,7 @@
settings:useAdminDisabledSummary="true" />
+ * Displays Wifi (full Slice) and Airplane mode.
+ *
+ */
+public class InternetConnectivityPanel implements PanelContent {
+
+ @VisibleForTesting
+ static final Uri AIRPLANE_URI = new Uri.Builder()
+ .scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(SettingsSlicesContract.AUTHORITY)
+ .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
+ .appendPath(SettingsSlicesContract.KEY_AIRPLANE_MODE)
+ .build();
+
+ private final Context mContext;
+
+ public static InternetConnectivityPanel create(Context context) {
+ return new InternetConnectivityPanel(context);
+ }
+
+ private InternetConnectivityPanel(Context context) {
+ mContext = context.getApplicationContext();
+ }
+
+ @Override
+ public String getTitle() {
+ return (String) mContext.getText(R.string.internet_connectivity_panel_title);
+ }
+
+ @Override
+ public List getSlices() {
+ final List uris = new ArrayList<>();
+ uris.add(WifiSlice.WIFI_URI);
+ uris.add(AIRPLANE_URI);
+ return uris;
+ }
+
+ @Override
+ public Intent getSeeMoreIntent() {
+ return null;
+ }
+}
diff --git a/src/com/android/settings/panel/PanelContent.java b/src/com/android/settings/panel/PanelContent.java
new file mode 100644
index 00000000000..bd84c2fa617
--- /dev/null
+++ b/src/com/android/settings/panel/PanelContent.java
@@ -0,0 +1,49 @@
+/*
+ * 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.panel;
+
+import android.content.Intent;
+import android.net.Uri;
+
+import java.util.List;
+
+/**
+ * Represents the data class needed to create a Settings Panel. See {@link PanelFragment}.
+ */
+public interface PanelContent {
+
+ /**
+ * @return a string for the title of the Panel.
+ */
+ CharSequence getTitle();
+
+ /**
+ * @return an ordered list of the Slices to be displayed in the Panel. The first item in the
+ * list is shown on top of the Panel.
+ */
+ List getSlices();
+
+
+ /**
+ * @return an {@link Intent} to the full content in Settings that is summarized by the Panel.
+ *
+ *
+ * For example, for the connectivity panel you would intent to the Network & Internet page.
+ *
+ */
+ Intent getSeeMoreIntent();
+}
diff --git a/src/com/android/settings/panel/PanelFeatureProvider.java b/src/com/android/settings/panel/PanelFeatureProvider.java
new file mode 100644
index 00000000000..7d6c5581d25
--- /dev/null
+++ b/src/com/android/settings/panel/PanelFeatureProvider.java
@@ -0,0 +1,27 @@
+/*
+ * 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.panel;
+
+import android.content.Context;
+
+public interface PanelFeatureProvider {
+
+ /**
+ * Returns {@link PanelContent} as specified by the {@param panelType}.
+ */
+ PanelContent getPanel(Context context, String panelType);
+}
diff --git a/src/com/android/settings/panel/PanelFeatureProviderImpl.java b/src/com/android/settings/panel/PanelFeatureProviderImpl.java
new file mode 100644
index 00000000000..2e840786b7f
--- /dev/null
+++ b/src/com/android/settings/panel/PanelFeatureProviderImpl.java
@@ -0,0 +1,32 @@
+/*
+ * 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.panel;
+
+import android.content.Context;
+
+public class PanelFeatureProviderImpl implements PanelFeatureProvider {
+
+ @Override
+ public PanelContent getPanel(Context context, String panelType) {
+ switch (panelType) {
+ case SettingsPanelActivity.PANEL_TYPE_WIFI:
+ return InternetConnectivityPanel.create(context);
+ }
+
+ throw new IllegalStateException("No matching panel for: " + panelType);
+ }
+}
diff --git a/src/com/android/settings/panel/PanelFragment.java b/src/com/android/settings/panel/PanelFragment.java
new file mode 100644
index 00000000000..bbdaec351f4
--- /dev/null
+++ b/src/com/android/settings/panel/PanelFragment.java
@@ -0,0 +1,88 @@
+/*
+ * 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.panel;
+
+import android.net.Uri;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import android.widget.LinearLayout;
+
+import androidx.lifecycle.LiveData;
+import androidx.slice.Slice;
+import androidx.slice.widget.SliceLiveData;
+import androidx.slice.widget.SliceView;
+
+import com.android.settings.R;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentActivity;
+
+import com.android.settings.overlay.FeatureFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class PanelFragment extends Fragment {
+
+ private static final String TAG = "PanelFragment";
+
+ private List mSliceViewList;
+ private List> mSliceDataList;
+ private LinearLayout mPanelLayout;
+
+ public PanelFragment() {
+ mSliceViewList = new ArrayList<>();
+ mSliceDataList = new ArrayList<>();
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ final FragmentActivity activity = getActivity();
+ final View view = inflater.inflate(R.layout.panel_layout, container, false);
+
+ mPanelLayout = view.findViewById(R.id.panel_parent_layout);
+ final Bundle arguments = getArguments();
+
+ final String panelType = arguments.getString(SettingsPanelActivity.KEY_PANEL_TYPE_ARGUMENT);
+
+ final PanelContent panel = FeatureFactory.getFactory(activity)
+ .getPanelFeatureProvider()
+ .getPanel(activity, panelType);
+
+ activity.setTitle(panel.getTitle());
+
+
+ for (Uri uri : panel.getSlices()) {
+ final SliceView sliceView = new SliceView(activity);
+ mPanelLayout.addView(sliceView);
+ final LiveData liveData = SliceLiveData.fromUri(activity, uri);
+ liveData.observe(this /* lifecycleOwner */, sliceView);
+
+ mSliceDataList.add(liveData);
+ mSliceViewList.add(sliceView);
+ }
+
+ return view;
+ }
+}
diff --git a/src/com/android/settings/panel/SettingsPanelActivity.java b/src/com/android/settings/panel/SettingsPanelActivity.java
new file mode 100644
index 00000000000..db1f60d3e49
--- /dev/null
+++ b/src/com/android/settings/panel/SettingsPanelActivity.java
@@ -0,0 +1,103 @@
+/*
+ * 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.panel;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.os.Bundle;
+
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.Window;
+import android.view.WindowManager;
+
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentActivity;
+import androidx.fragment.app.FragmentManager;
+
+import com.android.settings.R;
+
+/**
+ * Dialog Activity to host Settings Slices.
+ *
+ * TODO link to action / framework API
+ */
+public class SettingsPanelActivity extends FragmentActivity {
+
+ private final String TAG = "panel_activity";
+
+ /**
+ * Key specifying which Panel the app is requesting.
+ */
+ public static final String KEY_PANEL_TYPE_ARGUMENT = "PANEL_TYPE_ARGUMENT";
+
+
+ // TODO (b/117804442) move to framework
+ public static final String EXTRA_PANEL_TYPE = "com.android.settings.panel.extra";
+
+ // TODO (b/117804442) move to framework
+ public static final String PANEL_TYPE_WIFI = "wifi_panel";
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ final ComponentName callingActivityName = getCallingActivity();
+
+ if (callingActivityName == null) {
+ Log.e(TAG, "Must start with startActivityForResult. Closing.");
+ finish();
+ return;
+ }
+
+ final Intent callingIntent = getIntent();
+ if (callingIntent == null) {
+ Log.e(TAG, "Null intent, closing Panel Activity");
+ finish();
+ return;
+ }
+
+ final String typeExtra = callingIntent.getStringExtra(EXTRA_PANEL_TYPE);
+ if (TextUtils.isEmpty(typeExtra)) {
+ Log.e(TAG, "No intent passed, closing Panel Activity");
+ return;
+ }
+
+ setContentView(R.layout.settings_panel);
+
+ // Move the window to the bottom of screen, and make it take up the entire screen width.
+ final Window window = getWindow();
+ window.setGravity(Gravity.BOTTOM);
+ window.setLayout(WindowManager.LayoutParams.MATCH_PARENT,
+ WindowManager.LayoutParams.WRAP_CONTENT);
+
+
+ final Bundle bundle = new Bundle();
+ bundle.putString(KEY_PANEL_TYPE_ARGUMENT, typeExtra);
+
+ final PanelFragment panelFragment = new PanelFragment();
+ panelFragment.setArguments(bundle);
+
+ final FragmentManager fragmentManager = getSupportFragmentManager();
+ final Fragment fragment = fragmentManager.findFragmentById(R.id.main_content);
+ if (fragment == null) {
+ fragmentManager.beginTransaction().add(R.id.main_content, panelFragment).commit();
+ }
+ }
+}
diff --git a/src/com/android/settings/slices/SettingsSliceProvider.java b/src/com/android/settings/slices/SettingsSliceProvider.java
index fa669bb451a..952fc8b4596 100644
--- a/src/com/android/settings/slices/SettingsSliceProvider.java
+++ b/src/com/android/settings/slices/SettingsSliceProvider.java
@@ -427,7 +427,7 @@ public class SettingsSliceProvider extends SliceProvider {
try {
sliceData = mSlicesDatabaseAccessor.getSliceDataFromUri(uri);
} catch (IllegalStateException e) {
- Log.d(TAG, "Could not create slicedata for uri: " + uri);
+ Log.d(TAG, "Could not create slicedata for uri: " + uri, e);
return;
}
diff --git a/src/com/android/settings/slices/SlicesFeatureProviderImpl.java b/src/com/android/settings/slices/SlicesFeatureProviderImpl.java
index 39d385eb9d4..508eb1c1d78 100644
--- a/src/com/android/settings/slices/SlicesFeatureProviderImpl.java
+++ b/src/com/android/settings/slices/SlicesFeatureProviderImpl.java
@@ -1,3 +1,19 @@
+/*
+ * 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;
diff --git a/tests/robotests/assets/grandfather_not_implementing_instrumentable b/tests/robotests/assets/grandfather_not_implementing_instrumentable
index 625d9ffecf2..04ef0ef9594 100644
--- a/tests/robotests/assets/grandfather_not_implementing_instrumentable
+++ b/tests/robotests/assets/grandfather_not_implementing_instrumentable
@@ -6,4 +6,5 @@ com.android.settings.password.ChooseLockPattern$SaveAndFinishWorker
com.android.settings.RestrictedListPreference$RestrictedListPreferenceDialogFragment
com.android.settings.password.ConfirmDeviceCredentialBaseFragment$LastTryDialog
com.android.settings.password.CredentialCheckResultTracker
-com.android.settings.dashboard.profileselector.ProfileSelectDialog
\ No newline at end of file
+com.android.settings.dashboard.profileselector.ProfileSelectDialog
+com.android.settings.panel.PanelFragment
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/network/AirplaneModePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/network/AirplaneModePreferenceControllerTest.java
index 91a545c595d..46d177f88a9 100644
--- a/tests/robotests/src/com/android/settings/network/AirplaneModePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/network/AirplaneModePreferenceControllerTest.java
@@ -187,11 +187,4 @@ public class AirplaneModePreferenceControllerTest {
new AirplaneModePreferenceController(mContext,"toggle_airplane");
assertThat(controller.isSliceable()).isTrue();
}
-
- @Test
- public void isSliceableIncorrectKey_returnsFalse() {
- final AirplaneModePreferenceController controller =
- new AirplaneModePreferenceController(mContext, "bad_key");
- assertThat(controller.isSliceable()).isFalse();
- }
}
diff --git a/tests/robotests/src/com/android/settings/panel/InternetConnectivityPanelTest.java b/tests/robotests/src/com/android/settings/panel/InternetConnectivityPanelTest.java
new file mode 100644
index 00000000000..3e210f5298c
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/panel/InternetConnectivityPanelTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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.panel;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.net.Uri;
+
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.wifi.WifiSlice;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RuntimeEnvironment;
+
+import java.util.List;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+
+public class InternetConnectivityPanelTest {
+
+ private InternetConnectivityPanel mPanel;
+
+ @Before
+ public void setUp() {
+ mPanel = InternetConnectivityPanel.create(RuntimeEnvironment.application);
+ }
+
+ @Test
+ public void getSlices_containsNecessarySlices() {
+ final List uris = mPanel.getSlices();
+
+ assertThat(uris).containsExactly(WifiSlice.WIFI_URI,
+ InternetConnectivityPanel.AIRPLANE_URI);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/panel/PanelFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/panel/PanelFeatureProviderImplTest.java
new file mode 100644
index 00000000000..050fd0cd899
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/panel/PanelFeatureProviderImplTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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.panel;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+public class PanelFeatureProviderImplTest {
+
+ private Context mContext;
+ private PanelFeatureProviderImpl mProvider;
+
+ @Before
+ public void setUp() {
+ mContext = RuntimeEnvironment.application;
+ mProvider = new PanelFeatureProviderImpl();
+ }
+
+ @Test
+ public void getPanel_internetConnectivityKey_returnsCorrectPanel() {
+ final PanelContent panel = mProvider.getPanel(mContext,
+ SettingsPanelActivity.PANEL_TYPE_WIFI);
+
+ assertThat(panel).isInstanceOf(InternetConnectivityPanel.class);
+ }
+
+}
diff --git a/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java b/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java
index 24db8298c80..e14ef1f4b9d 100644
--- a/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java
+++ b/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java
@@ -33,6 +33,7 @@ import com.android.settings.overlay.DockUpdaterFeatureProvider;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.overlay.SupportFeatureProvider;
import com.android.settings.overlay.SurveyFeatureProvider;
+import com.android.settings.panel.PanelFeatureProvider;
import com.android.settings.search.SearchFeatureProvider;
import com.android.settings.security.SecurityFeatureProvider;
import com.android.settings.slices.SlicesFeatureProvider;
@@ -61,6 +62,7 @@ public class FakeFeatureFactory extends FeatureFactory {
public final UserFeatureProvider userFeatureProvider;
public final AssistGestureFeatureProvider assistGestureFeatureProvider;
public final AccountFeatureProvider mAccountFeatureProvider;
+ public final PanelFeatureProvider mPanelFeatureProvider;
public SlicesFeatureProvider slicesFeatureProvider;
public SearchFeatureProvider searchFeatureProvider;
@@ -102,6 +104,7 @@ public class FakeFeatureFactory extends FeatureFactory {
assistGestureFeatureProvider = mock(AssistGestureFeatureProvider.class);
slicesFeatureProvider = mock(SlicesFeatureProvider.class);
mAccountFeatureProvider = mock(AccountFeatureProvider.class);
+ mPanelFeatureProvider = mock(PanelFeatureProvider.class);
}
@Override
@@ -183,4 +186,9 @@ public class FakeFeatureFactory extends FeatureFactory {
public AccountFeatureProvider getAccountFeatureProvider() {
return mAccountFeatureProvider;
}
+
+ @Override
+ public PanelFeatureProvider getPanelFeatureProvider() {
+ return mPanelFeatureProvider;
+ }
}