/*
|
* Copyright (C) 2014 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.systemui.qs;
|
|
import android.app.ActivityManager;
|
import android.app.AlertDialog;
|
import android.app.admin.DevicePolicyEventLogger;
|
import android.content.Context;
|
import android.content.DialogInterface;
|
import android.content.Intent;
|
import android.content.pm.UserInfo;
|
import android.os.Handler;
|
import android.os.Looper;
|
import android.os.Message;
|
import android.os.UserManager;
|
import android.provider.Settings;
|
import android.text.SpannableStringBuilder;
|
import android.text.method.LinkMovementMethod;
|
import android.text.style.ClickableSpan;
|
import android.util.Log;
|
import android.util.StatsLog;
|
import android.view.ContextThemeWrapper;
|
import android.view.LayoutInflater;
|
import android.view.View;
|
import android.view.View.OnClickListener;
|
import android.view.ViewGroup;
|
import android.view.Window;
|
import android.widget.ImageView;
|
import android.widget.TextView;
|
|
import com.android.systemui.Dependency;
|
import com.android.systemui.FontSizeUtils;
|
import com.android.systemui.R;
|
import com.android.systemui.plugins.ActivityStarter;
|
import com.android.systemui.statusbar.phone.SystemUIDialog;
|
import com.android.systemui.statusbar.policy.SecurityController;
|
|
public class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListener {
|
protected static final String TAG = "QSSecurityFooter";
|
protected static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
|
|
private final View mRootView;
|
private final TextView mFooterText;
|
private final ImageView mFooterIcon;
|
private final Context mContext;
|
private final Callback mCallback = new Callback();
|
private final SecurityController mSecurityController;
|
private final ActivityStarter mActivityStarter;
|
private final Handler mMainHandler;
|
private final View mDivider;
|
|
private final UserManager mUm;
|
|
private AlertDialog mDialog;
|
private QSTileHost mHost;
|
protected H mHandler;
|
|
private boolean mIsVisible;
|
private CharSequence mFooterTextContent = null;
|
private int mFooterTextId;
|
private int mFooterIconId;
|
|
public QSSecurityFooter(QSPanel qsPanel, Context context) {
|
mRootView = LayoutInflater.from(context)
|
.inflate(R.layout.quick_settings_footer, qsPanel, false);
|
mRootView.setOnClickListener(this);
|
mFooterText = (TextView) mRootView.findViewById(R.id.footer_text);
|
mFooterIcon = (ImageView) mRootView.findViewById(R.id.footer_icon);
|
mFooterIconId = R.drawable.ic_info_outline;
|
mContext = context;
|
mMainHandler = new Handler(Looper.myLooper());
|
mActivityStarter = Dependency.get(ActivityStarter.class);
|
mSecurityController = Dependency.get(SecurityController.class);
|
mHandler = new H(Dependency.get(Dependency.BG_LOOPER));
|
mDivider = qsPanel == null ? null : qsPanel.getDivider();
|
mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
|
}
|
|
public void setHostEnvironment(QSTileHost host) {
|
mHost = host;
|
}
|
|
public void setListening(boolean listening) {
|
if (listening) {
|
mSecurityController.addCallback(mCallback);
|
refreshState();
|
} else {
|
mSecurityController.removeCallback(mCallback);
|
}
|
}
|
|
public void onConfigurationChanged() {
|
FontSizeUtils.updateFontSize(mFooterText, R.dimen.qs_tile_text_size);
|
}
|
|
public View getView() {
|
return mRootView;
|
}
|
|
public boolean hasFooter() {
|
return mRootView.getVisibility() != View.GONE;
|
}
|
|
@Override
|
public void onClick(View v) {
|
mHandler.sendEmptyMessage(H.CLICK);
|
}
|
|
private void handleClick() {
|
showDeviceMonitoringDialog();
|
DevicePolicyEventLogger
|
.createEvent(StatsLog.DEVICE_POLICY_EVENT__EVENT_ID__DO_USER_INFO_CLICKED)
|
.write();
|
}
|
|
public void showDeviceMonitoringDialog() {
|
mHost.collapsePanels();
|
// TODO: Delay dialog creation until after panels are collapsed.
|
createDialog();
|
}
|
|
public void refreshState() {
|
mHandler.sendEmptyMessage(H.REFRESH_STATE);
|
}
|
|
private void handleRefreshState() {
|
final boolean isDeviceManaged = mSecurityController.isDeviceManaged();
|
final UserInfo currentUser = mUm.getUserInfo(ActivityManager.getCurrentUser());
|
final boolean isDemoDevice = UserManager.isDeviceInDemoMode(mContext) && currentUser != null
|
&& currentUser.isDemo();
|
final boolean hasWorkProfile = mSecurityController.hasWorkProfile();
|
final boolean hasCACerts = mSecurityController.hasCACertInCurrentUser();
|
final boolean hasCACertsInWorkProfile = mSecurityController.hasCACertInWorkProfile();
|
final boolean isNetworkLoggingEnabled = mSecurityController.isNetworkLoggingEnabled();
|
final String vpnName = mSecurityController.getPrimaryVpnName();
|
final String vpnNameWorkProfile = mSecurityController.getWorkProfileVpnName();
|
final CharSequence organizationName = mSecurityController.getDeviceOwnerOrganizationName();
|
final CharSequence workProfileName = mSecurityController.getWorkProfileOrganizationName();
|
// Update visibility of footer
|
mIsVisible = (isDeviceManaged && !isDemoDevice) || hasCACerts || hasCACertsInWorkProfile ||
|
vpnName != null || vpnNameWorkProfile != null;
|
// Update the string
|
mFooterTextContent = getFooterText(isDeviceManaged, hasWorkProfile,
|
hasCACerts, hasCACertsInWorkProfile, isNetworkLoggingEnabled, vpnName,
|
vpnNameWorkProfile, organizationName, workProfileName);
|
// Update the icon
|
int footerIconId = R.drawable.ic_info_outline;
|
if (vpnName != null || vpnNameWorkProfile != null) {
|
if (mSecurityController.isVpnBranded()) {
|
footerIconId = R.drawable.stat_sys_branded_vpn;
|
} else {
|
footerIconId = R.drawable.stat_sys_vpn_ic;
|
}
|
}
|
if (mFooterIconId != footerIconId) {
|
mFooterIconId = footerIconId;
|
mMainHandler.post(mUpdateIcon);
|
}
|
mMainHandler.post(mUpdateDisplayState);
|
}
|
|
protected CharSequence getFooterText(boolean isDeviceManaged, boolean hasWorkProfile,
|
boolean hasCACerts, boolean hasCACertsInWorkProfile, boolean isNetworkLoggingEnabled,
|
String vpnName, String vpnNameWorkProfile, CharSequence organizationName,
|
CharSequence workProfileName) {
|
if (isDeviceManaged) {
|
if (hasCACerts || hasCACertsInWorkProfile || isNetworkLoggingEnabled) {
|
if (organizationName == null) {
|
return mContext.getString(
|
R.string.quick_settings_disclosure_management_monitoring);
|
}
|
return mContext.getString(
|
R.string.quick_settings_disclosure_named_management_monitoring,
|
organizationName);
|
}
|
if (vpnName != null && vpnNameWorkProfile != null) {
|
if (organizationName == null) {
|
return mContext.getString(R.string.quick_settings_disclosure_management_vpns);
|
}
|
return mContext.getString(R.string.quick_settings_disclosure_named_management_vpns,
|
organizationName);
|
}
|
if (vpnName != null || vpnNameWorkProfile != null) {
|
if (organizationName == null) {
|
return mContext.getString(
|
R.string.quick_settings_disclosure_management_named_vpn,
|
vpnName != null ? vpnName : vpnNameWorkProfile);
|
}
|
return mContext.getString(
|
R.string.quick_settings_disclosure_named_management_named_vpn,
|
organizationName,
|
vpnName != null ? vpnName : vpnNameWorkProfile);
|
}
|
if (organizationName == null) {
|
return mContext.getString(R.string.quick_settings_disclosure_management);
|
}
|
return mContext.getString(R.string.quick_settings_disclosure_named_management,
|
organizationName);
|
} // end if(isDeviceManaged)
|
if (hasCACertsInWorkProfile) {
|
if (workProfileName == null) {
|
return mContext.getString(
|
R.string.quick_settings_disclosure_managed_profile_monitoring);
|
}
|
return mContext.getString(
|
R.string.quick_settings_disclosure_named_managed_profile_monitoring,
|
workProfileName);
|
}
|
if (hasCACerts) {
|
return mContext.getString(R.string.quick_settings_disclosure_monitoring);
|
}
|
if (vpnName != null && vpnNameWorkProfile != null) {
|
return mContext.getString(R.string.quick_settings_disclosure_vpns);
|
}
|
if (vpnNameWorkProfile != null) {
|
return mContext.getString(R.string.quick_settings_disclosure_managed_profile_named_vpn,
|
vpnNameWorkProfile);
|
}
|
if (vpnName != null) {
|
if (hasWorkProfile) {
|
return mContext.getString(
|
R.string.quick_settings_disclosure_personal_profile_named_vpn,
|
vpnName);
|
}
|
return mContext.getString(R.string.quick_settings_disclosure_named_vpn,
|
vpnName);
|
}
|
return null;
|
}
|
|
@Override
|
public void onClick(DialogInterface dialog, int which) {
|
if (which == DialogInterface.BUTTON_NEGATIVE) {
|
final Intent intent = new Intent(Settings.ACTION_ENTERPRISE_PRIVACY_SETTINGS);
|
mDialog.dismiss();
|
mActivityStarter.postStartActivityDismissingKeyguard(intent, 0);
|
}
|
}
|
|
private void createDialog() {
|
final boolean isDeviceManaged = mSecurityController.isDeviceManaged();
|
final boolean hasWorkProfile = mSecurityController.hasWorkProfile();
|
final CharSequence deviceOwnerOrganization =
|
mSecurityController.getDeviceOwnerOrganizationName();
|
final boolean hasCACerts = mSecurityController.hasCACertInCurrentUser();
|
final boolean hasCACertsInWorkProfile = mSecurityController.hasCACertInWorkProfile();
|
final boolean isNetworkLoggingEnabled = mSecurityController.isNetworkLoggingEnabled();
|
final String vpnName = mSecurityController.getPrimaryVpnName();
|
final String vpnNameWorkProfile = mSecurityController.getWorkProfileVpnName();
|
|
mDialog = new SystemUIDialog(mContext);
|
mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
|
View dialogView = LayoutInflater.from(
|
new ContextThemeWrapper(mContext, R.style.Theme_SystemUI_Dialog))
|
.inflate(R.layout.quick_settings_footer_dialog, null, false);
|
mDialog.setView(dialogView);
|
mDialog.setButton(DialogInterface.BUTTON_POSITIVE, getPositiveButton(), this);
|
|
// device management section
|
CharSequence managementMessage = getManagementMessage(isDeviceManaged,
|
deviceOwnerOrganization);
|
if (managementMessage == null) {
|
dialogView.findViewById(R.id.device_management_disclosures).setVisibility(View.GONE);
|
} else {
|
dialogView.findViewById(R.id.device_management_disclosures).setVisibility(View.VISIBLE);
|
TextView deviceManagementWarning =
|
(TextView) dialogView.findViewById(R.id.device_management_warning);
|
deviceManagementWarning.setText(managementMessage);
|
mDialog.setButton(DialogInterface.BUTTON_NEGATIVE, getSettingsButton(), this);
|
}
|
|
// ca certificate section
|
CharSequence caCertsMessage = getCaCertsMessage(isDeviceManaged, hasCACerts,
|
hasCACertsInWorkProfile);
|
if (caCertsMessage == null) {
|
dialogView.findViewById(R.id.ca_certs_disclosures).setVisibility(View.GONE);
|
} else {
|
dialogView.findViewById(R.id.ca_certs_disclosures).setVisibility(View.VISIBLE);
|
TextView caCertsWarning = (TextView) dialogView.findViewById(R.id.ca_certs_warning);
|
caCertsWarning.setText(caCertsMessage);
|
// Make "Open trusted credentials"-link clickable
|
caCertsWarning.setMovementMethod(new LinkMovementMethod());
|
}
|
|
// network logging section
|
CharSequence networkLoggingMessage = getNetworkLoggingMessage(isNetworkLoggingEnabled);
|
if (networkLoggingMessage == null) {
|
dialogView.findViewById(R.id.network_logging_disclosures).setVisibility(View.GONE);
|
} else {
|
dialogView.findViewById(R.id.network_logging_disclosures).setVisibility(View.VISIBLE);
|
TextView networkLoggingWarning =
|
(TextView) dialogView.findViewById(R.id.network_logging_warning);
|
networkLoggingWarning.setText(networkLoggingMessage);
|
}
|
|
// vpn section
|
CharSequence vpnMessage = getVpnMessage(isDeviceManaged, hasWorkProfile, vpnName,
|
vpnNameWorkProfile);
|
if (vpnMessage == null) {
|
dialogView.findViewById(R.id.vpn_disclosures).setVisibility(View.GONE);
|
} else {
|
dialogView.findViewById(R.id.vpn_disclosures).setVisibility(View.VISIBLE);
|
TextView vpnWarning = (TextView) dialogView.findViewById(R.id.vpn_warning);
|
vpnWarning.setText(vpnMessage);
|
// Make "Open VPN Settings"-link clickable
|
vpnWarning.setMovementMethod(new LinkMovementMethod());
|
}
|
|
// Note: if a new section is added, should update configSubtitleVisibility to include
|
// the handling of the subtitle
|
configSubtitleVisibility(managementMessage != null,
|
caCertsMessage != null,
|
networkLoggingMessage != null,
|
vpnMessage != null,
|
dialogView);
|
|
mDialog.show();
|
mDialog.getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT,
|
ViewGroup.LayoutParams.WRAP_CONTENT);
|
}
|
|
protected void configSubtitleVisibility(boolean showDeviceManagement, boolean showCaCerts,
|
boolean showNetworkLogging, boolean showVpn, View dialogView) {
|
// Device Management title should always been shown
|
// When there is a Device Management message, all subtitles should be shown
|
if (showDeviceManagement) {
|
return;
|
}
|
// Hide the subtitle if there is only 1 message shown
|
int mSectionCountExcludingDeviceMgt = 0;
|
if (showCaCerts) { mSectionCountExcludingDeviceMgt++; }
|
if (showNetworkLogging) { mSectionCountExcludingDeviceMgt++; }
|
if (showVpn) { mSectionCountExcludingDeviceMgt++; }
|
|
// No work needed if there is no sections or more than 1 section
|
if (mSectionCountExcludingDeviceMgt != 1) {
|
return;
|
}
|
if (showCaCerts) {
|
dialogView.findViewById(R.id.ca_certs_subtitle).setVisibility(View.GONE);
|
}
|
if (showNetworkLogging) {
|
dialogView.findViewById(R.id.network_logging_subtitle).setVisibility(View.GONE);
|
}
|
if (showVpn) {
|
dialogView.findViewById(R.id.vpn_subtitle).setVisibility(View.GONE);
|
}
|
}
|
|
private String getSettingsButton() {
|
return mContext.getString(R.string.monitoring_button_view_policies);
|
}
|
|
private String getPositiveButton() {
|
return mContext.getString(R.string.ok);
|
}
|
|
protected CharSequence getManagementMessage(boolean isDeviceManaged,
|
CharSequence organizationName) {
|
if (!isDeviceManaged) return null;
|
if (organizationName != null)
|
return mContext.getString(
|
R.string.monitoring_description_named_management, organizationName);
|
return mContext.getString(R.string.monitoring_description_management);
|
}
|
|
protected CharSequence getCaCertsMessage(boolean isDeviceManaged, boolean hasCACerts,
|
boolean hasCACertsInWorkProfile) {
|
if (!(hasCACerts || hasCACertsInWorkProfile)) return null;
|
if (isDeviceManaged) {
|
return mContext.getString(R.string.monitoring_description_management_ca_certificate);
|
}
|
if (hasCACertsInWorkProfile) {
|
return mContext.getString(
|
R.string.monitoring_description_managed_profile_ca_certificate);
|
}
|
return mContext.getString(R.string.monitoring_description_ca_certificate);
|
}
|
|
protected CharSequence getNetworkLoggingMessage(boolean isNetworkLoggingEnabled) {
|
if (!isNetworkLoggingEnabled) return null;
|
return mContext.getString(R.string.monitoring_description_management_network_logging);
|
}
|
|
protected CharSequence getVpnMessage(boolean isDeviceManaged, boolean hasWorkProfile,
|
String vpnName, String vpnNameWorkProfile) {
|
if (vpnName == null && vpnNameWorkProfile == null) return null;
|
final SpannableStringBuilder message = new SpannableStringBuilder();
|
if (isDeviceManaged) {
|
if (vpnName != null && vpnNameWorkProfile != null) {
|
message.append(mContext.getString(R.string.monitoring_description_two_named_vpns,
|
vpnName, vpnNameWorkProfile));
|
} else {
|
message.append(mContext.getString(R.string.monitoring_description_named_vpn,
|
vpnName != null ? vpnName : vpnNameWorkProfile));
|
}
|
} else {
|
if (vpnName != null && vpnNameWorkProfile != null) {
|
message.append(mContext.getString(R.string.monitoring_description_two_named_vpns,
|
vpnName, vpnNameWorkProfile));
|
} else if (vpnNameWorkProfile != null) {
|
message.append(mContext.getString(
|
R.string.monitoring_description_managed_profile_named_vpn,
|
vpnNameWorkProfile));
|
} else if (hasWorkProfile) {
|
message.append(mContext.getString(
|
R.string.monitoring_description_personal_profile_named_vpn, vpnName));
|
} else {
|
message.append(mContext.getString(R.string.monitoring_description_named_vpn,
|
vpnName));
|
}
|
}
|
message.append(mContext.getString(R.string.monitoring_description_vpn_settings_separator));
|
message.append(mContext.getString(R.string.monitoring_description_vpn_settings),
|
new VpnSpan(), 0);
|
return message;
|
}
|
|
private int getTitle(String deviceOwner) {
|
if (deviceOwner != null) {
|
return R.string.monitoring_title_device_owned;
|
} else {
|
return R.string.monitoring_title;
|
}
|
}
|
|
private final Runnable mUpdateIcon = new Runnable() {
|
@Override
|
public void run() {
|
mFooterIcon.setImageResource(mFooterIconId);
|
}
|
};
|
|
private final Runnable mUpdateDisplayState = new Runnable() {
|
@Override
|
public void run() {
|
if (mFooterTextContent != null) {
|
mFooterText.setText(mFooterTextContent);
|
}
|
mRootView.setVisibility(mIsVisible ? View.VISIBLE : View.GONE);
|
if (mDivider != null) mDivider.setVisibility(mIsVisible ? View.GONE : View.VISIBLE);
|
}
|
};
|
|
private class Callback implements SecurityController.SecurityControllerCallback {
|
@Override
|
public void onStateChanged() {
|
refreshState();
|
}
|
}
|
|
private class H extends Handler {
|
private static final int CLICK = 0;
|
private static final int REFRESH_STATE = 1;
|
|
private H(Looper looper) {
|
super(looper);
|
}
|
|
@Override
|
public void handleMessage(Message msg) {
|
String name = null;
|
try {
|
if (msg.what == REFRESH_STATE) {
|
name = "handleRefreshState";
|
handleRefreshState();
|
} else if (msg.what == CLICK) {
|
name = "handleClick";
|
handleClick();
|
}
|
} catch (Throwable t) {
|
final String error = "Error in " + name;
|
Log.w(TAG, error, t);
|
mHost.warn(error, t);
|
}
|
}
|
}
|
|
protected class VpnSpan extends ClickableSpan {
|
@Override
|
public void onClick(View widget) {
|
final Intent intent = new Intent(Settings.ACTION_VPN_SETTINGS);
|
mDialog.dismiss();
|
mActivityStarter.postStartActivityDismissingKeyguard(intent, 0);
|
}
|
|
// for testing, to compare two CharSequences containing VpnSpans
|
@Override
|
public boolean equals(Object object) {
|
return object instanceof VpnSpan;
|
}
|
|
@Override
|
public int hashCode() {
|
return 314159257; // prime
|
}
|
}
|
}
|