Commit 80bab13f authored by Nate Fischer's avatar Nate Fischer Committed by Commit Bot

AW: implement stub UI for flipping flags

No change to production behavior.

This adds a UI for toggling flags (similar to chrome://flags), although
nothing actually happens yet when toggling the UI. This CL only builds
the visual component of the UI, and adds a single flag to the list to
demonstrate how the UI will look. In follow up work, we'll plumb this
toggled list of flags to the WebViews embedded in other applications.

Bug: 981143
Test: Manual - open the DevUI, observe the flags UI works as intended
Change-Id: I00e8e3c5472e71af3cdb3b70e7992d25a38a16b3
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1910811Reviewed-by: default avatarRichard Coles <torne@chromium.org>
Commit-Queue: Nate Fischer <ntfschr@chromium.org>
Cr-Commit-Position: refs/heads/master@{#714740}
parent 42b4d13c
...@@ -735,10 +735,13 @@ android_library("common_crash_java") { ...@@ -735,10 +735,13 @@ android_library("common_crash_java") {
android_library("common_java") { android_library("common_java") {
java_files = [ java_files = [
"java/src/org/chromium/android_webview/common/AwResource.java", "java/src/org/chromium/android_webview/common/AwResource.java",
"java/src/org/chromium/android_webview/common/Flag.java",
"java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java",
"java/src/org/chromium/android_webview/common/services/ServiceNames.java", "java/src/org/chromium/android_webview/common/services/ServiceNames.java",
] ]
deps = [ deps = [
"//base:base_java", "//base:base_java",
"//third_party/android_deps:androidx_annotation_annotation_java",
] ]
} }
......
...@@ -38,6 +38,7 @@ android_library("apk_java") { ...@@ -38,6 +38,7 @@ android_library("apk_java") {
android_library("devui_java") { android_library("devui_java") {
java_files = [ java_files = [
"java/src/org/chromium/android_webview/devui/CrashesListActivity.java", "java/src/org/chromium/android_webview/devui/CrashesListActivity.java",
"java/src/org/chromium/android_webview/devui/FlagsActivity.java",
"java/src/org/chromium/android_webview/devui/MainActivity.java", "java/src/org/chromium/android_webview/devui/MainActivity.java",
"java/src/org/chromium/android_webview/devui/util/CrashInfoLoader.java", "java/src/org/chromium/android_webview/devui/util/CrashInfoLoader.java",
"java/src/org/chromium/android_webview/devui/util/NavigationMenuHelper.java", "java/src/org/chromium/android_webview/devui/util/NavigationMenuHelper.java",
...@@ -50,6 +51,7 @@ android_library("devui_java") { ...@@ -50,6 +51,7 @@ android_library("devui_java") {
":devui_resources", ":devui_resources",
":system_webview_manifest", ":system_webview_manifest",
"//android_webview:common_crash_java", "//android_webview:common_crash_java",
"//android_webview:common_java",
"//base:base_java", "//base:base_java",
"//components/minidump_uploader:minidump_uploader_java", "//components/minidump_uploader:minidump_uploader_java",
"//third_party/android_deps:androidx_annotation_annotation_java", "//third_party/android_deps:androidx_annotation_annotation_java",
......
...@@ -47,6 +47,12 @@ ...@@ -47,6 +47,12 @@
android:launchMode="singleTop" android:launchMode="singleTop"
android:process=":webview_apk"> {# Explicit process required for monochrome compatibility. #} android:process=":webview_apk"> {# Explicit process required for monochrome compatibility. #}
</activity> </activity>
<activity android:name="org.chromium.android_webview.devui.FlagsActivity"
android:label="WebView Flags"
android:theme="@style/Theme.DevUi.DayNight"
android:launchMode="singleTop"
android:process=":webview_apk"> {# Explicit process required for monochrome compatibility. #}
</activity>
<!-- End of WebView Developer UI Activties --> <!-- End of WebView Developer UI Activties -->
<activity android:name="org.chromium.android_webview.app.LicenseActivity" <activity android:name="org.chromium.android_webview.app.LicenseActivity"
......
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2019 The Chromium Authors. All rights reserved. Use of this
source code is governed by a BSD-style license that can be found in the
LICENSE file.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/activity_flags"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="10dp">
<!--suppress HardcodedText -->
<TextView
android:id="@+id/flags_warning"
android:text="WARNING: EXPERIMENTAL FEATURES AHEAD!"
android:textColor="@color/warningText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="5dp"
android:paddingTop="5dp"
android:textAppearance="?android:attr/textAppearanceLarge"/>
<TextView
android:id="@+id/flags_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="5dp"
android:paddingTop="5dp"
android:textAppearance="?android:attr/textAppearanceMedium"/>
<ListView
android:id="@+id/flags_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="25dp"/>
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2019 The Chromium Authors. All rights reserved. Use of this
source code is governed by a BSD-style license that can be found in the
LICENSE file.
-->
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/flag_states"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textIsSelectable="true"
android:textStyle="bold"
android:textAppearance="?android:attr/textAppearanceSmall"
android:padding="2dp" />
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2019 The Chromium Authors. All rights reserved. Use of this
source code is governed by a BSD-style license that can be found in the
LICENSE file.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/toggleable_flag"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:orientation="vertical">
<!-- horizontal divider -->
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?android:attr/listDivider"/>
<TextView
android:id="@+id/flag_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textIsSelectable="true"
android:textStyle="bold"
android:textAppearance="?android:attr/textAppearanceMedium"
android:paddingBottom="2dp"
android:paddingTop="2dp" />
<TextView
android:id="@+id/flag_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textIsSelectable="true"/>
<Spinner
android:id="@+id/flag_toggle"
android:spinnerMode="dialog"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</LinearLayout>
...@@ -14,6 +14,11 @@ ...@@ -14,6 +14,11 @@
android:title="WebView Crashes" android:title="WebView Crashes"
android:showAsAction="never"/> android:showAsAction="never"/>
<!--suppress HardcodedText --> <!--suppress HardcodedText -->
<item
android:id="@+id/nav_menu_flags_ui"
android:title="Experimental Flags"
android:showAsAction="never"/>
<!--suppress HardcodedText -->
<item <item
android:id="@+id/nav_menu_main_ui" android:id="@+id/nav_menu_main_ui"
android:title="Home" android:title="Home"
......
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2019 The Chromium Authors. All rights reserved. Use of this
source code is governed by a BSD-style license that can be found in the
LICENSE file.
-->
<resources>
<color name="warningText">#C5221F</color>
</resources>
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.android_webview.devui;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Spinner;
import android.widget.TextView;
import org.chromium.android_webview.common.Flag;
import org.chromium.android_webview.common.ProductionSupportedFlagList;
import org.chromium.android_webview.devui.util.NavigationMenuHelper;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* An activity to toggle experimental WebView flags/features.
*/
@SuppressLint("SetTextI18n")
public class FlagsActivity extends Activity {
// TODO(ntfschr): at the moment we're only writing to these sets. When we implement the service,
// we'll also read the contents to know what to send to embedded WebViews.
private final Set<Flag> mEnabledFlags = new HashSet<>();
private final Set<Flag> mDisabledFlags = new HashSet<>();
private static final String[] sFlagStates = {
"Default",
"Enabled",
"Disabled",
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_flags);
ListView flagsListView = findViewById(R.id.flags_list);
TextView flagsDescriptionView = findViewById(R.id.flags_description);
flagsDescriptionView.setText("By enabling these features, you could "
+ "lose app data or compromise your security or privacy. Enabled features apply to "
+ "WebViews across all apps on the device.");
List<Flag> flagsList = Arrays.asList(ProductionSupportedFlagList.sFlagList);
flagsListView.setAdapter(new FlagsListAdapter(flagsList));
}
private class FlagStateSpinnerSelectedListener implements AdapterView.OnItemSelectedListener {
private Flag mFlag;
FlagStateSpinnerSelectedListener(Flag flag) {
mFlag = flag;
}
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
switch (sFlagStates[position]) {
case "Default":
mEnabledFlags.remove(mFlag);
mDisabledFlags.remove(mFlag);
break;
case "Enabled":
mEnabledFlags.add(mFlag);
mDisabledFlags.remove(mFlag);
break;
case "Disabled":
mEnabledFlags.remove(mFlag);
mDisabledFlags.add(mFlag);
break;
}
// TODO(ntfschr): enable/disable enable) developer mode (when that's supported), based
// on whether (mEnabledFlags.isEmpty() && mDisabledFlags.isEmpty()).
}
@Override
public void onNothingSelected(AdapterView<?> parent) {}
}
/**
* Adapter to create rows of toggleable Flags.
*/
private class FlagsListAdapter extends ArrayAdapter<Flag> {
private final List<Flag> mFlagsList;
public FlagsListAdapter(List<Flag> flagsList) {
super(FlagsActivity.this, R.layout.toggleable_flag, flagsList);
mFlagsList = flagsList;
}
@Override
public View getView(int position, View view, ViewGroup parent) {
// If the the old view is already created then reuse it, else create a new one by layout
// inflation.
if (view == null) {
view = getLayoutInflater().inflate(R.layout.toggleable_flag, null);
}
TextView flagName = view.findViewById(R.id.flag_name);
TextView flagDescription = view.findViewById(R.id.flag_description);
Spinner flagToggle = view.findViewById(R.id.flag_toggle);
Flag flag = getItem(position);
flagName.setText(flag.getName());
flagDescription.setText(flag.getDescription());
ArrayAdapter<String> adapter =
new ArrayAdapter<>(FlagsActivity.this, R.layout.flag_states, sFlagStates);
adapter.setDropDownViewResource(android.R.layout.select_dialog_singlechoice);
flagToggle.setAdapter(adapter);
flagToggle.setOnItemSelectedListener(new FlagStateSpinnerSelectedListener(flag));
return view;
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
NavigationMenuHelper.inflate(this, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (NavigationMenuHelper.onOptionsItemSelected(this, item)) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
...@@ -9,6 +9,7 @@ import android.view.Menu; ...@@ -9,6 +9,7 @@ import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import org.chromium.android_webview.devui.CrashesListActivity; import org.chromium.android_webview.devui.CrashesListActivity;
import org.chromium.android_webview.devui.FlagsActivity;
import org.chromium.android_webview.devui.MainActivity; import org.chromium.android_webview.devui.MainActivity;
import org.chromium.android_webview.devui.R; import org.chromium.android_webview.devui.R;
...@@ -36,6 +37,8 @@ public final class NavigationMenuHelper { ...@@ -36,6 +37,8 @@ public final class NavigationMenuHelper {
public static boolean onOptionsItemSelected(Activity activity, MenuItem item) { public static boolean onOptionsItemSelected(Activity activity, MenuItem item) {
if (item.getItemId() == R.id.nav_menu_crash_ui) { if (item.getItemId() == R.id.nav_menu_crash_ui) {
activity.startActivity(new Intent(activity, CrashesListActivity.class)); activity.startActivity(new Intent(activity, CrashesListActivity.class));
} else if (item.getItemId() == R.id.nav_menu_flags_ui) {
activity.startActivity(new Intent(activity, FlagsActivity.class));
} else if (item.getItemId() == R.id.nav_menu_main_ui) { } else if (item.getItemId() == R.id.nav_menu_main_ui) {
activity.startActivity(new Intent(activity, MainActivity.class)); activity.startActivity(new Intent(activity, MainActivity.class));
} else { } else {
......
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.android_webview.common;
import androidx.annotation.NonNull;
/**
* Class to represent a commandline flag. This is used by the developer UI to pass flags-to-toggle
* over to the WebView implementation.
*/
public class Flag {
private final String mName;
private final String mDescription;
private final boolean mIsBaseFeature;
/**
* Creates a Flag which represents a {@code base::Feature}.
*/
public static Flag baseFeature(@NonNull String name, @NonNull String description) {
return new Flag(name, description, true);
}
/**
* Creates a Flag which represents a commandline switch.
*/
public static Flag commandLine(@NonNull String name, @NonNull String description) {
return new Flag(name, description, false);
}
/**
* Calls should use {@link #baseFeature(String, String)} or {@link
* #commandLine(String, String)} instead.
*/
private Flag(@NonNull String name, @NonNull String description, boolean isBaseFeature) {
mName = name;
mDescription = description;
mIsBaseFeature = isBaseFeature;
}
@NonNull
public String getName() {
return mName;
}
@NonNull
public String getDescription() {
return mDescription;
}
/**
* Indicates whether this is a {@code base::Feature} or a commandline flag.
*
* @return {@code true} if this is a {@code base::Feature}, {@code false} if this is a
* commandline flag.
*/
public boolean isBaseFeature() {
return mIsBaseFeature;
}
}
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.android_webview.common;
/**
* List of experimental features/flags supported for user devices. Add features/flags to this list
* with scrutiny: any feature/flag in this list can be enabled for production Android devices, and
* so it must not compromise the Android security model (i.e., WebView must still protect the app's
* private data from being user visible).
*
* <p>
* This lives in the common package so it can be shared by dev UI (to know which features/flags to
* display) as well as the WebView implementation (so it knows which features/flags are safe to
* honor).
*/
public final class ProductionSupportedFlagList {
// Do not instantiate this class.
private ProductionSupportedFlagList() {}
/**
* A list of commandline flags supported on user devices.
*/
public static final Flag[] sFlagList = {
Flag.commandLine("show-composited-layer-borders",
"Renders a border around compositor layers to help debug and study layer "
+ "compositing."),
Flag.commandLine("webview-log-js-console-messages",
"Mirrors JavaScript console messages to system logs."),
};
}
...@@ -1025,6 +1025,13 @@ ...@@ -1025,6 +1025,13 @@
<category android:name="android.intent.category.DEFAULT"/> <category android:name="android.intent.category.DEFAULT"/>
</intent-filter> </intent-filter>
</activity> </activity>
<activity
android:label="WebView
Flags"
android:launchMode="singleTop"
android:name="org.chromium.android_webview.devui.FlagsActivity"
android:process=":webview_apk"
android:theme="@style/Theme.DevUi.DayNight"/>
<activity <activity
android:enabled="false" android:enabled="false"
android:excludeFromRecents="true" android:excludeFromRecents="true"
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment