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