[Wi-Fi] Add basic UI structure for adding Wi-Fi for apps feature.
Add following changes:
1. Add intent receiver.
2. Add panel UI with icon, title, summary.
3. Add two buttons (save and cancel).
4. Add test case for activity and fragment
Bug: 136472483
Test: Add following test cases for checking button and package name in activity and fragment.
1. AddAppNetworksActivityTest
2. AddAppNetworksFragmentTest
Change-Id: I5515a96fa3feb0e3e6d68159b2c0dec0894c15ee
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index c2dcd15..4cdfa25 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -3053,6 +3053,18 @@
</intent-filter>
</activity>
+ <activity android:name=".wifi.addappnetworks.AddAppNetworksActivity"
+ android:label="@string/settings_panel_title"
+ android:theme="@style/Theme.Panel"
+ android:launchMode="singleInstance"
+ android:excludeFromRecents="true"
+ android:permission="android.permission.CHANGE_WIFI_STATE">
+ <intent-filter>
+ <action android:name="android.settings.WIFI_ADD_NETWORKS" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
<activity-alias
android:name="MediaOutputSlice"
android:label="@string/media_output_panel_title"
diff --git a/res/layout/wifi_add_app_networks.xml b/res/layout/wifi_add_app_networks.xml
new file mode 100644
index 0000000..2dc3993
--- /dev/null
+++ b/res/layout/wifi_add_app_networks.xml
@@ -0,0 +1,116 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2019 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
+ -->
+
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/panel_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@drawable/settings_panel_background">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:gravity="start|center_vertical"
+ android:clipToPadding="false"
+ android:paddingStart="12dp"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="start|center_vertical"
+ android:minWidth="68dp"
+ android:orientation="horizontal"
+ android:clipToPadding="false"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp">
+ <androidx.preference.internal.PreferenceImageView
+ android:id="@+id/app_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ settings:maxWidth="48dp"
+ settings:maxHeight="48dp"/>
+ </LinearLayout>
+
+ <RelativeLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:paddingTop="16dp"
+ android:paddingBottom="16dp">
+
+ <TextView
+ android:id="@+id/app_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:textAppearance="?android:attr/textAppearanceListItem"
+ android:ellipsize="marquee"/>
+
+ <TextView
+ android:id="@+id/app_summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/app_title"
+ android:layout_alignStart="@+id/app_title"
+ android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+ android:textColor="?android:attr/textColorSecondary"
+ android:maxLines="10"/>
+ </RelativeLayout>
+ </LinearLayout>
+
+ <include layout="@layout/horizontal_divider"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:paddingTop="8dp"
+ android:paddingBottom="8dp">
+
+ <Button
+ android:id="@+id/cancel"
+ style="@android:style/Widget.DeviceDefault.Button.Borderless.Colored"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:layout_marginStart="12dp"
+ android:text="@string/cancel"/>
+
+ <Space
+ android:layout_weight="1"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"/>
+
+ <Button
+ android:id="@+id/save"
+ style="@android:style/Widget.DeviceDefault.Button.Borderless.Colored"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:layout_marginEnd="12dp"
+ android:text="@string/save"/>
+ </LinearLayout>
+ </LinearLayout>
+</FrameLayout>
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 4242609..50cecfa 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -2338,6 +2338,10 @@
<string name="wifi_hotspot_configure_ap_text_summary">AndroidAP WPA2 PSK hotspot</string>
<!-- Default access point SSID used for tethering -->
<string name="wifi_tether_configure_ssid_default">AndroidHotspot</string>
+ <!-- Title for the panel of add Wi-Fi network from APP [CHAR LIMIT=50]-->
+ <string name="wifi_add_app_single_network_title">Save this network?</string>
+ <!-- Summary for the panel of add Wi-Fi network from APP [CHAR LIMIT=NONE]-->
+ <string name="wifi_add_app_single_network_summary"><xliff:g id="appName" example="ThirdPartyAppName">%1$s</xliff:g> would like to save a network to your phone</string>
<!-- Do not translate. Used for diagnostic screens, precise translation is not necessary
Wi-Fi Testing on the diagnostic screen-->
diff --git a/src/com/android/settings/wifi/addappnetworks/AddAppNetworksActivity.java b/src/com/android/settings/wifi/addappnetworks/AddAppNetworksActivity.java
new file mode 100644
index 0000000..0810b54
--- /dev/null
+++ b/src/com/android/settings/wifi/addappnetworks/AddAppNetworksActivity.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2019 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.wifi.addappnetworks;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.Gravity;
+import android.view.Window;
+import android.view.WindowManager;
+
+import androidx.annotation.Nullable;
+import androidx.fragment.app.FragmentActivity;
+import androidx.fragment.app.FragmentManager;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.settings.R;
+import com.android.settings.core.HideNonSystemOverlayMixin;
+
+/**
+ * When apps send a new intent with a WifiConfiguration list extra to Settings APP. Settings APP
+ * will launch this activity, which contains {@code AddAppNetworksFragment}, with a UI panel pop
+ * up asking the user if they approve the network being proposed by the app to be saved on to
+ * the device. User can decide to save or cancel the request.,
+ */
+public class AddAppNetworksActivity extends FragmentActivity {
+ public static final String TAG = "AddAppNetworksActivity";
+
+ private AddAppNetworksFragment mFragment;
+
+ /** Key specifying the package name of the apps which requested the Panel. */
+ public static final String KEY_CALLING_PACKAGE_NAME = "panel_calling_package_name";
+
+ @VisibleForTesting
+ final Bundle mBundle = new Bundle();
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.settings_panel);
+ showAddNetworksFragment();
+ getLifecycle().addObserver(new HideNonSystemOverlayMixin(this));
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+ setIntent(intent);
+ showAddNetworksFragment();
+ }
+
+ @VisibleForTesting
+ void showAddNetworksFragment() {
+ // 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);
+
+ // TODO: check the new intent status
+ mBundle.putString(KEY_CALLING_PACKAGE_NAME, getCallingPackage());
+ final FragmentManager fragmentManager = getSupportFragmentManager();
+ if (fragmentManager.findFragmentByTag(TAG) == null) {
+ final AddAppNetworksFragment fragment = new AddAppNetworksFragment();
+ fragment.setArguments(mBundle);
+ fragmentManager.beginTransaction().add(R.id.main_content, fragment, TAG).commit();
+ }
+ }
+}
diff --git a/src/com/android/settings/wifi/addappnetworks/AddAppNetworksFragment.java b/src/com/android/settings/wifi/addappnetworks/AddAppNetworksFragment.java
new file mode 100644
index 0000000..281ca57
--- /dev/null
+++ b/src/com/android/settings/wifi/addappnetworks/AddAppNetworksFragment.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2019 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.wifi.addappnetworks;
+
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.FragmentActivity;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.settings.R;
+import com.android.settings.Utils;
+import com.android.settings.core.InstrumentedFragment;
+
+import java.util.ArrayList;
+
+/**
+ * The Fragment list those networks, which is proposed by other app, to user, and handle user's
+ * choose on either saving those networks or rejecting the request.
+ */
+public class AddAppNetworksFragment extends InstrumentedFragment {
+ public static final String TAG = "AddAppNetworksFragment";
+
+ private TextView mTitleView;
+ private TextView mSummaryView;
+ private ImageView mIconView;
+
+ @VisibleForTesting
+ Button mCancelButton;
+ @VisibleForTesting
+ Button mSaveButton;
+ @VisibleForTesting
+ String mCallingPackageName;
+
+ @Nullable
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ return inflater.inflate(R.layout.wifi_add_app_networks, container, false);
+ }
+
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+
+ // init local variable.
+ mTitleView = view.findViewById(R.id.app_title);
+ mIconView = view.findViewById(R.id.app_icon);
+ mCancelButton = view.findViewById(R.id.cancel);
+ mSaveButton = view.findViewById(R.id.save);
+ mSummaryView = view.findViewById(R.id.app_summary);
+
+ // Assigns button listeners.
+ mCancelButton.setOnClickListener(getCancelListener());
+ mSaveButton.setOnClickListener(getSaveListener());
+
+ final Bundle bundle = getArguments();
+ createContent(bundle);
+ }
+
+ private void createContent(Bundle bundle) {
+ final FragmentActivity activity = getActivity();
+
+ // Assigns caller app icon and summary.
+ mCallingPackageName =
+ bundle.getString(AddAppNetworksActivity.KEY_CALLING_PACKAGE_NAME);
+ assignAppIcon(activity, mCallingPackageName);
+ assignTitleAndSummary(activity, mCallingPackageName);
+ }
+
+ private void assignAppIcon(Context context, String callingPackageName) {
+ final Drawable drawable = loadPackageIconDrawable(context, callingPackageName);
+ mIconView.setImageDrawable(drawable);
+ }
+
+ private Drawable loadPackageIconDrawable(Context context, String callingPackageName) {
+ Drawable icon = null;
+ try {
+ icon = context.getPackageManager().getApplicationIcon(callingPackageName);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.d(TAG, "Cannot get application icon", e);
+ }
+
+ return icon;
+ }
+
+ private void assignTitleAndSummary(Context context, String callingPackageName) {
+ // Assigns caller app name to title
+ mTitleView.setText(getTitle());
+
+ // Set summary
+ mSummaryView.setText(getAddNetworkRequesterSummary(
+ Utils.getApplicationLabel(context, callingPackageName)));
+ }
+
+ private CharSequence getAddNetworkRequesterSummary(CharSequence appName) {
+ return getString(R.string.wifi_add_app_single_network_summary, appName);
+ }
+
+ private CharSequence getTitle() {
+ return getString(R.string.wifi_add_app_single_network_title);
+ }
+
+ View.OnClickListener getCancelListener() {
+ return (v) -> {
+ Log.d(TAG, "User rejected to add network");
+ };
+ }
+
+ View.OnClickListener getSaveListener() {
+ return (v) -> {
+ Log.d(TAG, "User agree to add networks");
+ };
+ }
+
+ private void finishWithResult(int resultCode, ArrayList<Integer> resultArrayList) {
+ if (resultArrayList != null) {
+ Intent intent = new Intent();
+ intent.putIntegerArrayListExtra(Settings.EXTRA_WIFI_CONFIGURATION_RESULT_LIST,
+ resultArrayList);
+ getActivity().setResult(resultCode, intent);
+ }
+ getActivity().finish();
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ // TODO(b/144891278): Need to define a new metric for this page, use the WIFI item first.
+ return SettingsEnums.WIFI;
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/wifi/addappnetworks/AddAppNetworksActivityTest.java b/tests/robotests/src/com/android/settings/wifi/addappnetworks/AddAppNetworksActivityTest.java
new file mode 100644
index 0000000..b614698
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/wifi/addappnetworks/AddAppNetworksActivityTest.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2019 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.wifi.addappnetworks;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.robolectric.Shadows.shadowOf;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class AddAppNetworksActivityTest {
+
+ @Test
+ public void startActivity_withPackageName_bundleShouldHaveRightPackageName() {
+ final String packageName = RuntimeEnvironment.application.getPackageName();
+ final AddAppNetworksActivity activity =
+ Robolectric.buildActivity(AddAppNetworksActivity.class).create().get();
+ shadowOf(activity).setCallingPackage(packageName);
+
+ activity.showAddNetworksFragment();
+
+ assertThat(activity.mBundle.getString(AddAppNetworksActivity.KEY_CALLING_PACKAGE_NAME))
+ .isEqualTo(packageName);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/wifi/addappnetworks/AddAppNetworksFragmentTest.java b/tests/robotests/src/com/android/settings/wifi/addappnetworks/AddAppNetworksFragmentTest.java
new file mode 100644
index 0000000..38ddf33
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/wifi/addappnetworks/AddAppNetworksFragmentTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2019 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.wifi.addappnetworks;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+import android.os.Bundle;
+
+import androidx.fragment.app.FragmentActivity;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.shadows.androidx.fragment.FragmentController;
+
+@RunWith(RobolectricTestRunner.class)
+public class AddAppNetworksFragmentTest {
+ private static final String FAKE_APP_NAME = "fake_app_name";
+ private FragmentActivity mActivity;
+ private AddAppNetworksFragment mAddAppNetworksFragment;
+ private Context mContext;
+
+ @Before
+ public void setUp() {
+ mAddAppNetworksFragment = spy(new AddAppNetworksFragment());
+ MockitoAnnotations.initMocks(this);
+
+ // Set up bundle
+ final Bundle bundle = new Bundle();
+ bundle.putString(AddAppNetworksActivity.KEY_CALLING_PACKAGE_NAME, FAKE_APP_NAME);
+ doReturn(bundle).when(mAddAppNetworksFragment).getArguments();
+
+ FragmentController.setupFragment(mAddAppNetworksFragment);
+ }
+
+ @Test
+ public void callingPackageName_onCreateView_shouldBeCorrect() {
+ assertThat(mAddAppNetworksFragment.mCallingPackageName).isEqualTo(FAKE_APP_NAME);
+ }
+
+ @Test
+ public void launchFragment_shouldShowSaveButton() {
+ assertThat(mAddAppNetworksFragment.mSaveButton).isNotNull();
+ }
+
+ @Test
+ public void launchFragment_shouldShowCancelButton() {
+ assertThat(mAddAppNetworksFragment.mCancelButton).isNotNull();
+ }
+}