[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
This commit is contained in:
govenliu
2019-11-21 09:36:58 +08:00
parent 9096329b22
commit d507b56ad1
7 changed files with 487 additions and 0 deletions

View File

@@ -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"

View File

@@ -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>

View File

@@ -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-->

View File

@@ -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();
}
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}