/* * 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.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.UserHandle; import android.provider.Settings; import android.text.SpannableStringBuilder; import android.text.method.LinkMovementMethod; import android.text.style.ClickableSpan; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.View.OnClickListener; import android.widget.ImageView; import android.widget.TextView; import com.android.systemui.FontSizeUtils; import com.android.systemui.R; import com.android.systemui.statusbar.phone.QSTileHost; import com.android.systemui.statusbar.phone.SystemUIDialog; import com.android.systemui.statusbar.policy.SecurityController; import static android.provider.Settings.ACTION_VPN_SETTINGS; public class QSFooter implements OnClickListener, DialogInterface.OnClickListener { protected static final String TAG = "QSFooter"; protected static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private final View mRootView; private final TextView mFooterText; private final ImageView mFooterIcon; private final ImageView mFooterIcon2; private final Context mContext; private final Callback mCallback = new Callback(); private SecurityController mSecurityController; private AlertDialog mDialog; private QSTileHost mHost; private Handler mHandler; private final Handler mMainHandler; private boolean mIsVisible; private boolean mIsIconVisible; private boolean mIsIcon2Visible; private int mFooterTextId; private int mFooterIconId; private int mFooterIcon2Id; public QSFooter(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); mFooterIcon2 = (ImageView) mRootView.findViewById(R.id.footer_icon2); mFooterIconId = R.drawable.ic_qs_vpn; mFooterIcon2Id = R.drawable.ic_qs_network_logging; mContext = context; mMainHandler = new Handler(); } public void setHost(QSTileHost host) { mHost = host; mSecurityController = host.getSecurityController(); mHandler = new H(host.getLooper()); } public void setListening(boolean listening) { if (listening) { mSecurityController.addCallback(mCallback); } 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(); } 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() { // If the device has device owner, show "Device may be monitored", but -- // TODO See b/25779452 -- device owner doesn't actually have monitoring power. boolean isVpnEnabled = mSecurityController.isVpnEnabled(); boolean isNetworkLoggingEnabled = mSecurityController.isNetworkLoggingEnabled(); mIsIconVisible = isVpnEnabled || isNetworkLoggingEnabled; mIsIcon2Visible = isVpnEnabled && isNetworkLoggingEnabled; if (mSecurityController.isDeviceManaged()) { mFooterTextId = R.string.device_owned_footer; mIsVisible = true; int footerIconId = isVpnEnabled ? R.drawable.ic_qs_vpn : R.drawable.ic_qs_network_logging; if (mFooterIconId != footerIconId) { mFooterIconId = footerIconId; mMainHandler.post(mUpdateIcon); } } else { boolean isBranded = mSecurityController.isVpnBranded(); mFooterTextId = isBranded ? R.string.branded_vpn_footer : R.string.vpn_footer; // Update the VPN footer icon, if needed. int footerIconId = isVpnEnabled ? (isBranded ? R.drawable.ic_qs_branded_vpn : R.drawable.ic_qs_vpn) : R.drawable.ic_qs_network_logging; if (mFooterIconId != footerIconId) { mFooterIconId = footerIconId; mMainHandler.post(mUpdateIcon); } mIsVisible = mIsIconVisible; } mMainHandler.post(mUpdateDisplayState); } @Override public void onClick(DialogInterface dialog, int which) { if (which == DialogInterface.BUTTON_NEGATIVE) { final Intent settingsIntent = new Intent(ACTION_VPN_SETTINGS); mHost.startActivityDismissingKeyguard(settingsIntent); } } private void createDialog() { final String deviceOwnerPackage = mSecurityController.getDeviceOwnerName(); final String profileOwnerPackage = mSecurityController.getProfileOwnerName(); final boolean isNetworkLoggingEnabled = mSecurityController.isNetworkLoggingEnabled(); final String primaryVpn = mSecurityController.getPrimaryVpnName(); final String profileVpn = mSecurityController.getProfileVpnName(); boolean hasProfileOwner = mSecurityController.hasProfileOwner(); boolean isBranded = deviceOwnerPackage == null && mSecurityController.isVpnBranded(); mDialog = new SystemUIDialog(mContext); if (!isBranded) { mDialog.setTitle(getTitle(deviceOwnerPackage)); } CharSequence msg = getMessage(deviceOwnerPackage, profileOwnerPackage, primaryVpn, profileVpn, hasProfileOwner, isBranded); if (deviceOwnerPackage == null) { mDialog.setMessage(msg); if (mSecurityController.isVpnEnabled() && !mSecurityController.isVpnRestricted()) { mDialog.setButton(DialogInterface.BUTTON_NEGATIVE, getSettingsButton(), this); } } else { View dialogView = LayoutInflater.from(mContext) .inflate(R.layout.quick_settings_footer_dialog, null, false); mDialog.setView(dialogView); TextView deviceOwnerWarning = (TextView) dialogView.findViewById(R.id.device_owner_warning); deviceOwnerWarning.setText(msg); // Make the link "learn more" clickable. deviceOwnerWarning.setMovementMethod(new LinkMovementMethod()); if (primaryVpn == null) { dialogView.findViewById(R.id.vpn_icon).setVisibility(View.GONE); dialogView.findViewById(R.id.vpn_subtitle).setVisibility(View.GONE); dialogView.findViewById(R.id.vpn_warning).setVisibility(View.GONE); } else { final SpannableStringBuilder message = new SpannableStringBuilder(); message.append(mContext.getString(R.string.monitoring_description_do_body_vpn, primaryVpn)); if (!mSecurityController.isVpnRestricted()) { message.append(mContext.getString( R.string.monitoring_description_vpn_settings_separator)); message.append(mContext.getString(R.string.monitoring_description_vpn_settings), new VpnSpan(), 0); } TextView vpnWarning = (TextView) dialogView.findViewById(R.id.vpn_warning); vpnWarning.setText(message); // Make the link "Open VPN Settings" clickable. vpnWarning.setMovementMethod(new LinkMovementMethod()); } if (!isNetworkLoggingEnabled) { dialogView.findViewById(R.id.network_logging_icon).setVisibility(View.GONE); dialogView.findViewById(R.id.network_logging_subtitle).setVisibility(View.GONE); dialogView.findViewById(R.id.network_logging_warning).setVisibility(View.GONE); } } mDialog.setButton(DialogInterface.BUTTON_POSITIVE, getPositiveButton(isBranded), this); mDialog.show(); mDialog.getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); } private String getSettingsButton() { return mContext.getString(R.string.status_bar_settings_settings_button); } private String getPositiveButton(boolean isBranded) { return mContext.getString(isBranded ? android.R.string.ok : R.string.quick_settings_done); } protected CharSequence getMessage(String deviceOwnerPackage, String profileOwnerPackage, String primaryVpn, String profileVpn, boolean hasProfileOwner, boolean isBranded) { if (deviceOwnerPackage != null) { return mContext.getString(R.string.monitoring_description_device_owned, deviceOwnerPackage); } else if (primaryVpn != null) { if (profileVpn != null) { return mContext.getString(R.string.monitoring_description_app_personal_work, profileOwnerPackage, profileVpn, primaryVpn); } else { if (isBranded) { return mContext.getString(R.string.branded_monitoring_description_app_personal, primaryVpn); } else { return mContext.getString(R.string.monitoring_description_app_personal, primaryVpn); } } } else if (profileVpn != null) { return mContext.getString(R.string.monitoring_description_app_work, profileOwnerPackage, profileVpn); } else if (profileOwnerPackage != null && hasProfileOwner) { return mContext.getString(R.string.monitoring_description_device_owned, profileOwnerPackage); } else { // No device owner, no personal VPN, no work VPN, no user owner. Why are we here? return null; } } 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); mFooterIcon2.setImageResource(mFooterIcon2Id); } }; private final Runnable mUpdateDisplayState = new Runnable() { @Override public void run() { if (mFooterTextId != 0) { mFooterText.setText(mFooterTextId); } mRootView.setVisibility(mIsVisible ? View.VISIBLE : View.GONE); mFooterIcon.setVisibility(mIsIconVisible ? View.VISIBLE : View.INVISIBLE); mFooterIcon2.setVisibility(mIsIcon2Visible ? View.VISIBLE : View.INVISIBLE); } }; 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); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); mDialog.dismiss(); mContext.startActivity(intent); } } }