1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.systemui.qs; 17 18 import android.app.AlertDialog; 19 import android.content.Context; 20 import android.content.DialogInterface; 21 import android.content.Intent; 22 import android.graphics.drawable.Drawable; 23 import android.os.Handler; 24 import android.os.Looper; 25 import android.os.Message; 26 import android.os.UserHandle; 27 import android.util.Log; 28 import android.view.LayoutInflater; 29 import android.view.View; 30 import android.view.View.OnClickListener; 31 import android.widget.ImageView; 32 import android.widget.TextView; 33 34 import com.android.systemui.FontSizeUtils; 35 import com.android.systemui.R; 36 import com.android.systemui.statusbar.phone.QSTileHost; 37 import com.android.systemui.statusbar.phone.SystemUIDialog; 38 import com.android.systemui.statusbar.policy.SecurityController; 39 40 import static android.provider.Settings.ACTION_VPN_SETTINGS; 41 42 public class QSFooter implements OnClickListener, DialogInterface.OnClickListener { 43 protected static final String TAG = "QSFooter"; 44 protected static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 45 46 private final View mRootView; 47 private final TextView mFooterText; 48 private final ImageView mFooterIcon; 49 private final Context mContext; 50 private final Callback mCallback = new Callback(); 51 52 private SecurityController mSecurityController; 53 private AlertDialog mDialog; 54 private QSTileHost mHost; 55 private Handler mHandler; 56 private final Handler mMainHandler; 57 58 private boolean mIsVisible; 59 private boolean mIsIconVisible; 60 private int mFooterTextId; 61 private int mFooterIconId; 62 QSFooter(QSPanel qsPanel, Context context)63 public QSFooter(QSPanel qsPanel, Context context) { 64 mRootView = LayoutInflater.from(context) 65 .inflate(R.layout.quick_settings_footer, qsPanel, false); 66 mRootView.setOnClickListener(this); 67 mFooterText = (TextView) mRootView.findViewById(R.id.footer_text); 68 mFooterIcon = (ImageView) mRootView.findViewById(R.id.footer_icon); 69 mFooterIconId = R.drawable.ic_qs_vpn; 70 mContext = context; 71 mMainHandler = new Handler(); 72 } 73 setHost(QSTileHost host)74 public void setHost(QSTileHost host) { 75 mHost = host; 76 mSecurityController = host.getSecurityController(); 77 mHandler = new H(host.getLooper()); 78 } 79 setListening(boolean listening)80 public void setListening(boolean listening) { 81 if (listening) { 82 mSecurityController.addCallback(mCallback); 83 } else { 84 mSecurityController.removeCallback(mCallback); 85 } 86 } 87 onConfigurationChanged()88 public void onConfigurationChanged() { 89 FontSizeUtils.updateFontSize(mFooterText, R.dimen.qs_tile_text_size); 90 } 91 getView()92 public View getView() { 93 return mRootView; 94 } 95 hasFooter()96 public boolean hasFooter() { 97 return mRootView.getVisibility() != View.GONE; 98 } 99 100 @Override onClick(View v)101 public void onClick(View v) { 102 mHandler.sendEmptyMessage(H.CLICK); 103 } 104 handleClick()105 private void handleClick() { 106 mHost.collapsePanels(); 107 // TODO: Delay dialog creation until after panels are collapsed. 108 createDialog(); 109 } 110 refreshState()111 public void refreshState() { 112 mHandler.sendEmptyMessage(H.REFRESH_STATE); 113 } 114 handleRefreshState()115 private void handleRefreshState() { 116 mIsIconVisible = mSecurityController.isVpnEnabled(); 117 // If the device has device owner, show "Device may be monitored", but -- 118 // TODO See b/25779452 -- device owner doesn't actually have monitoring power. 119 if (mSecurityController.isDeviceManaged()) { 120 mFooterTextId = R.string.device_owned_footer; 121 mIsVisible = true; 122 } else { 123 mFooterTextId = R.string.vpn_footer; 124 // Update the VPN footer icon, if needed. 125 int footerIconId = (mSecurityController.isVpnBranded() 126 ? R.drawable.ic_qs_branded_vpn 127 : R.drawable.ic_qs_vpn); 128 if (mFooterIconId != footerIconId) { 129 mFooterIconId = footerIconId; 130 mMainHandler.post(mUpdateIcon); 131 } 132 mIsVisible = mIsIconVisible; 133 } 134 mMainHandler.post(mUpdateDisplayState); 135 } 136 137 @Override onClick(DialogInterface dialog, int which)138 public void onClick(DialogInterface dialog, int which) { 139 if (which == DialogInterface.BUTTON_NEGATIVE) { 140 final Intent settingsIntent = new Intent(ACTION_VPN_SETTINGS); 141 mHost.startActivityDismissingKeyguard(settingsIntent); 142 } 143 } 144 createDialog()145 private void createDialog() { 146 String deviceOwner = mSecurityController.getDeviceOwnerName(); 147 String profileOwner = mSecurityController.getProfileOwnerName(); 148 String primaryVpn = mSecurityController.getPrimaryVpnName(); 149 String profileVpn = mSecurityController.getProfileVpnName(); 150 boolean managed = mSecurityController.hasProfileOwner(); 151 152 mDialog = new SystemUIDialog(mContext); 153 mDialog.setTitle(getTitle(deviceOwner)); 154 mDialog.setMessage(getMessage(deviceOwner, profileOwner, primaryVpn, profileVpn, managed)); 155 mDialog.setButton(DialogInterface.BUTTON_POSITIVE, getPositiveButton(), this); 156 if (mSecurityController.isVpnEnabled() && !mSecurityController.isVpnRestricted()) { 157 mDialog.setButton(DialogInterface.BUTTON_NEGATIVE, getSettingsButton(), this); 158 } 159 mDialog.show(); 160 } 161 getSettingsButton()162 private String getSettingsButton() { 163 return mContext.getString(R.string.status_bar_settings_settings_button); 164 } 165 getPositiveButton()166 private String getPositiveButton() { 167 return mContext.getString(R.string.quick_settings_done); 168 } 169 getMessage(String deviceOwner, String profileOwner, String primaryVpn, String profileVpn, boolean primaryUserIsManaged)170 private String getMessage(String deviceOwner, String profileOwner, String primaryVpn, 171 String profileVpn, boolean primaryUserIsManaged) { 172 // Show a special warning when the device has device owner, but -- 173 // TODO See b/25779452 -- device owner doesn't actually have monitoring power. 174 if (deviceOwner != null) { 175 if (primaryVpn != null) { 176 return mContext.getString(R.string.monitoring_description_vpn_app_device_owned, 177 deviceOwner, primaryVpn); 178 } else { 179 return mContext.getString(R.string.monitoring_description_device_owned, 180 deviceOwner); 181 } 182 } else if (primaryVpn != null) { 183 if (profileVpn != null) { 184 return mContext.getString(R.string.monitoring_description_app_personal_work, 185 profileOwner, profileVpn, primaryVpn); 186 } else { 187 return mContext.getString(R.string.monitoring_description_app_personal, 188 primaryVpn); 189 } 190 } else if (profileVpn != null) { 191 return mContext.getString(R.string.monitoring_description_app_work, 192 profileOwner, profileVpn); 193 } else if (profileOwner != null && primaryUserIsManaged) { 194 return mContext.getString(R.string.monitoring_description_device_owned, 195 profileOwner); 196 } else { 197 // No device owner, no personal VPN, no work VPN, no user owner. Why are we here? 198 return null; 199 } 200 } 201 getTitle(String deviceOwner)202 private int getTitle(String deviceOwner) { 203 if (deviceOwner != null) { 204 return R.string.monitoring_title_device_owned; 205 } else { 206 return R.string.monitoring_title; 207 } 208 } 209 210 private final Runnable mUpdateIcon = new Runnable() { 211 @Override 212 public void run() { 213 mFooterIcon.setImageResource(mFooterIconId); 214 } 215 }; 216 217 private final Runnable mUpdateDisplayState = new Runnable() { 218 @Override 219 public void run() { 220 if (mFooterTextId != 0) { 221 mFooterText.setText(mFooterTextId); 222 } 223 mRootView.setVisibility(mIsVisible ? View.VISIBLE : View.GONE); 224 mFooterIcon.setVisibility(mIsIconVisible ? View.VISIBLE : View.INVISIBLE); 225 } 226 }; 227 228 private class Callback implements SecurityController.SecurityControllerCallback { 229 @Override onStateChanged()230 public void onStateChanged() { 231 refreshState(); 232 } 233 } 234 235 private class H extends Handler { 236 private static final int CLICK = 0; 237 private static final int REFRESH_STATE = 1; 238 H(Looper looper)239 private H(Looper looper) { 240 super(looper); 241 } 242 243 @Override handleMessage(Message msg)244 public void handleMessage(Message msg) { 245 String name = null; 246 try { 247 if (msg.what == REFRESH_STATE) { 248 name = "handleRefreshState"; 249 handleRefreshState(); 250 } else if (msg.what == CLICK) { 251 name = "handleClick"; 252 handleClick(); 253 } 254 } catch (Throwable t) { 255 final String error = "Error in " + name; 256 Log.w(TAG, error, t); 257 mHost.warn(error, t); 258 } 259 } 260 } 261 262 } 263