LockTaskNotify.java revision a8f569c10a423ced5be7e019c3df2bca11b052f5
1/* 2 * Copyright (C) 2013 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 17package com.android.server.am; 18 19import android.content.BroadcastReceiver; 20import android.content.Context; 21import android.content.Intent; 22import android.content.IntentFilter; 23import android.content.res.Resources; 24import android.graphics.BitmapFactory; 25import android.graphics.PixelFormat; 26import android.os.Handler; 27import android.os.Message; 28import android.text.Spannable; 29import android.text.SpannableString; 30import android.text.style.DynamicDrawableSpan; 31import android.text.style.ImageSpan; 32import android.util.DisplayMetrics; 33import android.util.Slog; 34import android.view.Gravity; 35import android.view.MotionEvent; 36import android.view.View; 37import android.view.ViewGroup; 38import android.view.WindowManager; 39import android.widget.FrameLayout; 40import android.widget.TextView; 41import android.widget.Toast; 42 43import com.android.internal.R; 44 45/** 46 * Helper to manage showing/hiding a image to notify them that they are entering 47 * or exiting lock-to-app mode. 48 */ 49public class LockTaskNotify { 50 private static final String TAG = "LockTaskNotify"; 51 52 private static final int SHOW_LENGTH_MS = 1500; 53 54 private final Context mContext; 55 private final H mHandler; 56 57 private ClingWindowView mClingWindow; 58 private WindowManager mWindowManager; 59 private boolean mIsStarting; 60 61 public LockTaskNotify(Context context) { 62 mContext = context; 63 mHandler = new H(); 64 mWindowManager = (WindowManager) 65 mContext.getSystemService(Context.WINDOW_SERVICE); 66 } 67 68 public void showToast(boolean isLocked) { 69 mHandler.obtainMessage(H.SHOW_TOAST, isLocked ? 1 : 0, 0 /* Not used */).sendToTarget(); 70 } 71 72 public void handleShowToast(boolean isLocked) { 73 final Resources r = Resources.getSystem(); 74 String text = mContext.getString(isLocked 75 ? R.string.lock_to_app_toast_locked : R.string.lock_to_app_toast); 76 Toast toast = Toast.makeText(mContext, text, Toast.LENGTH_LONG); 77 TextView tv = (TextView) toast.getView().findViewById(R.id.message); 78 79 if (isLocked) { 80 tv.setText(text); 81 } else { 82 final SpannableString formattedText = 83 new SpannableString(text.replace('$', ' ')); 84 final ImageSpan imageSpan = new ImageSpan(mContext, 85 BitmapFactory.decodeResource(r, R.drawable.ic_recent), 86 DynamicDrawableSpan.ALIGN_BOTTOM); 87 final int index = text.indexOf('$'); 88 if (index >= 0) { 89 formattedText.setSpan(imageSpan, index, index + 1, 90 Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 91 } 92 93 // Make icon fit. 94 final float width = imageSpan.getDrawable().getIntrinsicWidth(); 95 final float height = imageSpan.getDrawable().getIntrinsicHeight(); 96 final int lineHeight = tv.getLineHeight(); 97 imageSpan.getDrawable().setBounds(0, 0, (int) (lineHeight * width / height), 98 lineHeight); 99 100 tv.setText(formattedText); 101 } 102 103 104 toast.show(); 105 } 106 107 public void show(boolean starting) { 108 mIsStarting = starting; 109 mHandler.obtainMessage(H.SHOW).sendToTarget(); 110 } 111 112 public WindowManager.LayoutParams getClingWindowLayoutParams() { 113 final WindowManager.LayoutParams lp = new WindowManager.LayoutParams( 114 ViewGroup.LayoutParams.MATCH_PARENT, 115 ViewGroup.LayoutParams.MATCH_PARENT, 116 WindowManager.LayoutParams.TYPE_TOAST, 117 0 118 | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 119 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 120 | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED 121 , 122 PixelFormat.TRANSLUCENT); 123 lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; 124 lp.setTitle("LockTaskNotify"); 125 lp.windowAnimations = com.android.internal.R.style.Animation_RecentApplications; 126 lp.gravity = Gravity.FILL; 127 return lp; 128 } 129 130 public FrameLayout.LayoutParams getImageLayoutParams() { 131 return new FrameLayout.LayoutParams( 132 ViewGroup.LayoutParams.WRAP_CONTENT, 133 ViewGroup.LayoutParams.WRAP_CONTENT, 134 Gravity.CENTER_HORIZONTAL | Gravity.CENTER_VERTICAL); 135 } 136 137 private void handleShow() { 138 mClingWindow = new ClingWindowView(mContext); 139 140 // we will be hiding the nav bar, so layout as if it's already hidden 141 mClingWindow.setSystemUiVisibility( 142 View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION 143 | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); 144 145 // show the confirmation 146 WindowManager.LayoutParams lp = getClingWindowLayoutParams(); 147 mWindowManager.addView(mClingWindow, lp); 148 } 149 150 private void handleHide() { 151 if (mClingWindow != null) { 152 mWindowManager.removeView(mClingWindow); 153 mClingWindow = null; 154 } 155 } 156 157 158 private class ClingWindowView extends FrameLayout { 159 private View mView; 160 161 private Runnable mUpdateLayoutRunnable = new Runnable() { 162 @Override 163 public void run() { 164 if (mView != null && mView.getParent() != null) { 165 mView.setLayoutParams(getImageLayoutParams()); 166 } 167 } 168 }; 169 170 private BroadcastReceiver mReceiver = new BroadcastReceiver() { 171 @Override 172 public void onReceive(Context context, Intent intent) { 173 if (intent.getAction().equals(Intent.ACTION_CONFIGURATION_CHANGED)) { 174 post(mUpdateLayoutRunnable); 175 } 176 } 177 }; 178 179 public ClingWindowView(Context context) { 180 super(context); 181 setClickable(true); 182 } 183 184 @Override 185 public void onAttachedToWindow() { 186 super.onAttachedToWindow(); 187 188 DisplayMetrics metrics = new DisplayMetrics(); 189 mWindowManager.getDefaultDisplay().getMetrics(metrics); 190 191 int id = R.layout.lock_to_app_exit; 192 if (mIsStarting) { 193 id = R.layout.lock_to_app_enter; 194 } 195 mView = View.inflate(getContext(), id, null); 196 197 addView(mView, getImageLayoutParams()); 198 199 mContext.registerReceiver(mReceiver, 200 new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED)); 201 mHandler.sendMessageDelayed(mHandler.obtainMessage(H.HIDE), SHOW_LENGTH_MS); 202 } 203 204 @Override 205 public void onDetachedFromWindow() { 206 mContext.unregisterReceiver(mReceiver); 207 } 208 209 @Override 210 public boolean onTouchEvent(MotionEvent motion) { 211 Slog.v(TAG, "ClingWindowView.onTouchEvent"); 212 return true; 213 } 214 } 215 216 private final class H extends Handler { 217 private static final int SHOW = 1; 218 private static final int HIDE = 2; 219 private static final int SHOW_TOAST = 3; 220 221 @Override 222 public void handleMessage(Message msg) { 223 switch(msg.what) { 224 case SHOW: 225 handleShow(); 226 break; 227 case HIDE: 228 handleHide(); 229 break; 230 case SHOW_TOAST: 231 handleShowToast(msg.arg1 != 0); 232 break; 233 } 234 } 235 } 236} 237