Have WebView Implementation Dev Setting use DefaultAppPickerFragment.

Make the WebView Implementation Dev Setting look like the fullscreen
settings deriving from DefaultAppPickerFragment.
Point the Activity opened through Settings.ACTION_WEBVIEW_SETTINGS to
the class WebViewAppPicker which is our new implementation of the
WebView Implementation setting.

Ensure the new setting closes if it is reached from a user that isn't an
admin of the device.

Bug: 34806477
Bug: 34966439
Test: Ensure WebView implementation Dev Setting looks ok (disabled
packages should have a text showing why they cannot be chosen).
Test: Start Intent with action Settings.ACTION_WEBVIEW_SETTINGS and
ensure the started Activity looks similar to the WebView Implementation
Dev Setting.
Test: Ensure picking a package that is no longer active, updates the
setting (so that package isn't visible anymore).
Change-Id: I08007c515193739ad61dfd735bb5130fc07bd6e6
This commit is contained in:
Gustav Sennton
2017-02-03 16:19:02 +00:00
parent 8be6862021
commit 5b596285a0
15 changed files with 365 additions and 512 deletions

View File

@@ -1,138 +0,0 @@
/*
* Copyright (C) 2017 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.webview;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.RemoteException;
import android.graphics.Color;
import android.support.annotation.VisibleForTesting;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import com.android.settings.applications.AppViewHolder;
import com.android.settings.R;
import java.util.ArrayList;
import java.util.List;
/**
* Custom list adapter for Settings to choose WebView package.
* Note: parts of this class are copied from AppPicker.java.
*/
class WebViewAppListAdapter extends ArrayAdapter<WebViewApplicationInfo> {
private final LayoutInflater mInflater;
private final String mCurrentWebViewPackageName;
public WebViewAppListAdapter(Context context,
WebViewUpdateServiceWrapper webviewUpdateServiceWrapper) {
super(context, 0);
mInflater = LayoutInflater.from(context);
final List<WebViewApplicationInfo> packageInfoList =
new ArrayList<WebViewApplicationInfo>();
List<ApplicationInfo> pkgs =
webviewUpdateServiceWrapper.getValidWebViewApplicationInfos(getContext());
for (ApplicationInfo ai : pkgs) {
WebViewApplicationInfo info = new WebViewApplicationInfo(ai,
ai.loadLabel(context.getPackageManager()).toString(),
getDisabledReason(webviewUpdateServiceWrapper, context, ai.packageName));
packageInfoList.add(info);
}
addAll(packageInfoList);
PackageInfo currentWebViewPackage = webviewUpdateServiceWrapper.getCurrentWebViewPackage();
mCurrentWebViewPackageName =
currentWebViewPackage == null ? null : currentWebViewPackage.packageName;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// A ViewHolder keeps references to children views to avoid unnecessary calls
// to findViewById() on each row.
AppViewHolder holder = AppViewHolder.createOrRecycle(mInflater, convertView);
convertView = holder.rootView;
WebViewApplicationInfo info = getItem(position);
holder.appName.setText(info.label);
if (info.info != null) {
holder.appIcon.setImageDrawable(info.info.loadIcon(getContext().getPackageManager()));
// Allow disable-description to wrap - to be able to show several lines of text in case
// a package is disabled/uninstalled for several users.
holder.summary.setSingleLine(false);
if (!isEnabled(position)) {
holder.summary.setText(info.disabledReason);
} else {
holder.summary.setText("");
}
} else {
holder.appIcon.setImageDrawable(null);
holder.summary.setText("");
}
holder.disabled.setVisibility(View.GONE);
// Only allow a package to be chosen if it is enabled and installed for all users.
convertView.setEnabled(isEnabled(position));
if (info.info.packageName.equals(mCurrentWebViewPackageName)) {
convertView.setBackgroundColor(Color.GRAY);
} else {
convertView.setBackgroundColor(Color.WHITE);
}
return convertView;
}
@Override
public boolean isEnabled (int position) {
WebViewApplicationInfo info = getItem(position);
return info.disabledReason == null;
}
@Override
public boolean areAllItemsEnabled() {
int numItems = getCount();
for (int n = 0; n < numItems; n++) {
if (!isEnabled(n)) return false;
}
return true;
}
/**
* Returns the reason why a package cannot be used as WebView implementation.
* This is either because of it being disabled, uninstalled, or hidden for any user.
*/
@VisibleForTesting
static String getDisabledReason(WebViewUpdateServiceWrapper webviewUpdateServiceWrapper,
Context context, String packageName) {
StringBuilder disabledReason = new StringBuilder();
List<UserPackageWrapper> userPackages =
webviewUpdateServiceWrapper.getPackageInfosAllUsers(context, packageName);
for (UserPackageWrapper userPackage : userPackages) {
if (!userPackage.isInstalledPackage()) {
// Package uninstalled/hidden
disabledReason.append(context.getString(
R.string.webview_uninstalled_for_user, userPackage.getUserInfo().name));
} else if (!userPackage.isEnabledPackage()) {
// Package disabled
disabledReason.append(context.getString(
R.string.webview_disabled_for_user, userPackage.getUserInfo().name));
}
}
if (disabledReason.length() == 0) return null;
return disabledReason.toString();
}
}

View File

@@ -16,57 +16,83 @@
package com.android.settings.webview;
import android.app.ListActivity;
import static android.provider.Settings.ACTION_WEBVIEW_SETTINGS;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.RemoteException;
import android.support.annotation.VisibleForTesting;
import android.util.Log;
import android.view.View;
import android.webkit.WebViewFactory;
import android.widget.ListView;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.core.instrumentation.Instrumentable;
import com.android.settings.core.instrumentation.VisibilityLoggerMixin;
import com.android.settings.R;
import com.android.settings.applications.defaultapps.DefaultAppInfo;
import com.android.settings.applications.defaultapps.DefaultAppPickerFragment;
public class WebViewAppPicker extends ListActivity implements Instrumentable {
private static final String TAG = WebViewAppPicker.class.getSimpleName();
private WebViewAppListAdapter mAdapter;
import java.util.ArrayList;
import java.util.List;
public class WebViewAppPicker extends DefaultAppPickerFragment {
private WebViewUpdateServiceWrapper mWebViewUpdateServiceWrapper;
private final VisibilityLoggerMixin mVisibilityLoggerMixin =
new VisibilityLoggerMixin(getMetricsCategory());
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
private WebViewUpdateServiceWrapper getWebViewUpdateServiceWrapper() {
if (mWebViewUpdateServiceWrapper == null) {
setWebViewUpdateServiceWrapper(createDefaultWebViewUpdateServiceWrapper());
}
mAdapter = new WebViewAppListAdapter(this, mWebViewUpdateServiceWrapper);
setListAdapter(mAdapter);
mVisibilityLoggerMixin.onAttach(this);
return mWebViewUpdateServiceWrapper;
}
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
WebViewApplicationInfo app = mAdapter.getItem(position);
public void onAttach(Context context) {
super.onAttach(context);
if (mWebViewUpdateServiceWrapper.setWebViewProvider(app.info.packageName)) {
Intent intent = new Intent();
intent.setAction(app.info.packageName);
setResult(RESULT_OK, intent);
} else {
mWebViewUpdateServiceWrapper.showInvalidChoiceToast(this);
if (!mUserManager.isAdminUser()) {
getActivity().finish();
}
finish();
}
@Override
protected List<DefaultAppInfo> getCandidates() {
final List<DefaultAppInfo> packageInfoList = new ArrayList<DefaultAppInfo>();
List<ApplicationInfo> pkgs =
getWebViewUpdateServiceWrapper().getValidWebViewApplicationInfos(getContext());
for (ApplicationInfo ai : pkgs) {
packageInfoList.add(new DefaultAppInfo(ai,
getDisabledReason(getWebViewUpdateServiceWrapper(),
getContext(), ai.packageName)));
}
return packageInfoList;
}
@Override
protected String getDefaultAppKey() {
PackageInfo currentPackage = getWebViewUpdateServiceWrapper().getCurrentWebViewPackage();
return currentPackage == null ? null : currentPackage.packageName;
}
protected boolean setDefaultAppKey(String key) {
boolean success = getWebViewUpdateServiceWrapper().setWebViewProvider(key);
return success;
}
@Override
protected void onSelectionPerformed(boolean success) {
if (success) {
Activity activity = getActivity();
Intent intent = activity == null ? null : activity.getIntent();
if (intent != null && ACTION_WEBVIEW_SETTINGS.equals(intent.getAction())) {
// If this was started through ACTION_WEBVIEW_SETTINGS then return once we have
// chosen a new package.
getActivity().finish();
}
} else {
getWebViewUpdateServiceWrapper().showInvalidChoiceToast(getActivity());
updateCandidates();
}
}
private WebViewUpdateServiceWrapper createDefaultWebViewUpdateServiceWrapper() {
return new WebViewUpdateServiceWrapper();
}
@@ -76,20 +102,32 @@ public class WebViewAppPicker extends ListActivity implements Instrumentable {
mWebViewUpdateServiceWrapper = wvusWrapper;
}
@Override
public void onResume() {
super.onResume();
mVisibilityLoggerMixin.onResume();
}
@Override
public void onPause() {
super.onPause();
mVisibilityLoggerMixin.onPause();
}
@Override
public int getMetricsCategory() {
return MetricsEvent.WEBVIEW_IMPLEMENTATION;
}
/**
* Returns the reason why a package cannot be used as WebView implementation.
* This is either because of it being disabled, uninstalled, or hidden for any user.
*/
@VisibleForTesting
String getDisabledReason(WebViewUpdateServiceWrapper webviewUpdateServiceWrapper,
Context context, String packageName) {
StringBuilder disabledReason = new StringBuilder();
List<UserPackageWrapper> userPackages =
webviewUpdateServiceWrapper.getPackageInfosAllUsers(context, packageName);
for (UserPackageWrapper userPackage : userPackages) {
if (!userPackage.isInstalledPackage()) {
// Package uninstalled/hidden
return context.getString(
R.string.webview_uninstalled_for_user, userPackage.getUserInfo().name);
} else if (!userPackage.isEnabledPackage()) {
// Package disabled
return context.getString(
R.string.webview_disabled_for_user, userPackage.getUserInfo().name);
}
}
return null;
}
}

View File

@@ -14,18 +14,15 @@
package com.android.settings.webview;
import android.app.Activity;
import android.content.pm.PackageInfo;
import android.content.Context;
import android.content.Intent;
import android.support.annotation.VisibleForTesting;
import android.content.pm.PackageInfo;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceScreen;
import com.android.settings.DevelopmentSettings;
import com.android.settings.core.PreferenceController;
import com.android.settings.applications.defaultapps.DefaultAppInfo;
import com.android.settings.applications.defaultapps.DefaultAppPreferenceController;
public class WebViewAppPreferenceController extends PreferenceController {
public class WebViewAppPreferenceController extends DefaultAppPreferenceController {
private static final String WEBVIEW_APP_KEY = "select_webview_provider";
@@ -45,20 +42,9 @@ public class WebViewAppPreferenceController extends PreferenceController {
}
@Override
public boolean handlePreferenceTreeClick(Preference preference) {
if (getPreferenceKey().equals(preference.getKey())) {
return true;
}
return false;
}
public Intent getActivityIntent() {
return new Intent(mContext, WebViewAppPicker.class);
}
@Override
public void updateState(Preference preference) {
mPreference.setSummary(getCurrentWebViewPackageLabel(mContext));
public DefaultAppInfo getDefaultAppInfo() {
PackageInfo currentPackage = mWebViewUpdateServiceWrapper.getCurrentWebViewPackage();
return new DefaultAppInfo(currentPackage == null ? null : currentPackage.applicationInfo);
}
@Override
@@ -69,23 +55,6 @@ public class WebViewAppPreferenceController extends PreferenceController {
}
}
/**
* Handle the return-value from the WebViewAppPicker Activity.
*/
public void onActivityResult(int resultCode, Intent data) {
// Update the preference summary no matter whether we succeeded to change the webview
// implementation correctly - we might have changed implementation to one the user did not
// choose.
updateState(null);
}
private String getCurrentWebViewPackageLabel(Context context) {
PackageInfo webViewPackage = mWebViewUpdateServiceWrapper.getCurrentWebViewPackage();
if (webViewPackage == null) return "";
return webViewPackage.applicationInfo.loadLabel(context.getPackageManager()).toString();
}
@Override
public String getPreferenceKey() {
return WEBVIEW_APP_KEY;

View File

@@ -1,29 +0,0 @@
/*
* Copyright (C) 2017 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.webview;
import android.content.pm.ApplicationInfo;
final class WebViewApplicationInfo {
final ApplicationInfo info;
final String label;
final String disabledReason;
public WebViewApplicationInfo(ApplicationInfo info, String label, String disabledReason) {
this.info = info;
this.label = label;
this.disabledReason = disabledReason;
}
}