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.os.Handler; 23 import android.os.Looper; 24 import android.os.Message; 25 import android.os.UserHandle; 26 import android.util.Log; 27 import android.view.LayoutInflater; 28 import android.view.View; 29 import android.view.View.OnClickListener; 30 import android.widget.ImageView; 31 import android.widget.TextView; 32 33 import com.android.systemui.FontSizeUtils; 34 import com.android.systemui.R; 35 import com.android.systemui.statusbar.phone.QSTileHost; 36 import com.android.systemui.statusbar.phone.SystemUIDialog; 37 import com.android.systemui.statusbar.policy.SecurityController; 38 39 public class QSFooter implements OnClickListener, DialogInterface.OnClickListener { 40 protected static final String TAG = "QSFooter"; 41 protected static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 42 43 private static final String ACTION_VPN_SETTINGS = "android.net.vpn.SETTINGS"; 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 (mSecurityController.hasDeviceOwner()) { 115 mFooterTextId = R.string.device_owned_footer; 116 mIsVisible = true; 117 } else { 118 mFooterTextId = R.string.vpn_footer; 119 mIsVisible = mIsIconVisible; 120 } 121 mMainHandler.post(mUpdateDisplayState); 122 } 123 124 @Override 125 public void onClick(DialogInterface dialog, int which) { 126 if (which == DialogInterface.BUTTON_NEGATIVE) { 127 final Intent settingsIntent = new Intent(ACTION_VPN_SETTINGS); 128 mContext.startActivityAsUser(settingsIntent, UserHandle.CURRENT); 129 } 130 } 131 132 private void createDialog() { 133 String deviceOwner = mSecurityController.getDeviceOwnerName(); 134 String profileOwner = mSecurityController.getProfileOwnerName(); 135 String primaryVpn = mSecurityController.getPrimaryVpnName(); 136 String profileVpn = mSecurityController.getProfileVpnName(); 137 boolean managed = mSecurityController.hasProfileOwner(); 138 139 mDialog = new SystemUIDialog(mContext); 140 mDialog.setTitle(getTitle(deviceOwner)); 141 mDialog.setMessage(getMessage(deviceOwner, profileOwner, primaryVpn, profileVpn, managed)); 142 mDialog.setButton(DialogInterface.BUTTON_POSITIVE, getPositiveButton(), this); 143 if (mSecurityController.isVpnEnabled()) { 144 mDialog.setButton(DialogInterface.BUTTON_NEGATIVE, getNegativeButton(), this); 145 } 146 mDialog.show(); 147 } 148 149 private String getNegativeButton() { 150 return mContext.getString(R.string.status_bar_settings_settings_button); 151 } 152 153 private String getPositiveButton() { 154 return mContext.getString(R.string.quick_settings_done); 155 } 156 157 private String getMessage(String deviceOwner, String profileOwner, String primaryVpn, 158 String profileVpn, boolean primaryUserIsManaged) { 159 if (deviceOwner != null) { 160 if (primaryVpn != null) { 161 return mContext.getString(R.string.monitoring_description_vpn_app_device_owned, 162 deviceOwner, primaryVpn); 163 } else { 164 return mContext.getString(R.string.monitoring_description_device_owned, 165 deviceOwner); 166 } 167 } else if (primaryVpn != null) { 168 if (profileVpn != null) { 169 return mContext.getString(R.string.monitoring_description_app_personal_work, 170 profileOwner, profileVpn, primaryVpn); 171 } else { 172 return mContext.getString(R.string.monitoring_description_app_personal, 173 primaryVpn); 174 } 175 } else if (profileVpn != null) { 176 return mContext.getString(R.string.monitoring_description_app_work, 177 profileOwner, profileVpn); 178 } else if (profileOwner != null && primaryUserIsManaged) { 179 return mContext.getString(R.string.monitoring_description_device_owned, 180 profileOwner); 181 } else { 182 // No device owner, no personal VPN, no work VPN, no user owner. Why are we here? 183 return null; 184 } 185 } 186 187 private int getTitle(String deviceOwner) { 188 if (deviceOwner != null) { 189 return R.string.monitoring_title_device_owned; 190 } else { 191 return R.string.monitoring_title; 192 } 193 } 194 195 private final Runnable mUpdateDisplayState = new Runnable() { 196 @Override 197 public void run() { 198 if (mFooterTextId != 0) { 199 mFooterText.setText(mFooterTextId); 200 } 201 mRootView.setVisibility(mIsVisible ? View.VISIBLE : View.GONE); 202 mFooterIcon.setVisibility(mIsIconVisible ? View.VISIBLE : View.INVISIBLE); 203 } 204 }; 205 206 private class Callback implements SecurityController.SecurityControllerCallback { 207 @Override 208 public void onStateChanged() { 209 refreshState(); 210 } 211 } 212 213 private class H extends Handler { 214 private static final int CLICK = 0; 215 private static final int REFRESH_STATE = 1; 216 217 private H(Looper looper) { 218 super(looper); 219 } 220 221 @Override 222 public void handleMessage(Message msg) { 223 String name = null; 224 try { 225 if (msg.what == REFRESH_STATE) { 226 name = "handleRefreshState"; 227 handleRefreshState(); 228 } else if (msg.what == CLICK) { 229 name = "handleClick"; 230 handleClick(); 231 } 232 } catch (Throwable t) { 233 final String error = "Error in " + name; 234 Log.w(TAG, error, t); 235 mHost.warn(error, t); 236 } 237 } 238 } 239 240 } 241