package com.ittianyu.mobileguard.view; import android.content.Context; import android.graphics.PixelFormat; import android.graphics.Point; import android.view.Gravity; import android.view.MotionEvent; import android.view.View; import android.view.WindowManager; import com.ittianyu.mobileguard.constant.Constant; import com.ittianyu.mobileguard.utils.ConfigUtils; /** * Created by yu. * Like a Toast, show in front of window and can be dragged. * You can put any view in it. */ public class FloatToast { private WindowManager mWM; private View mView; private View mNextView; private final WindowManager.LayoutParams mParams = new WindowManager.LayoutParams(); private float startX, startY; /** * Construct an empty FloatToast object. */ private FloatToast(Context context) { // XXX This should be changed to use a Dialog, with a Theme.Toast // defined that sets up the layout params appropriately. final WindowManager.LayoutParams params = mParams; params.height = WindowManager.LayoutParams.WRAP_CONTENT; params.width = WindowManager.LayoutParams.WRAP_CONTENT; params.format = PixelFormat.TRANSLUCENT; // params.type = WindowManager.LayoutParams.TYPE_TOAST; params.type = WindowManager.LayoutParams.TYPE_PRIORITY_PHONE; params.setTitle("Toast"); params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; // set gravity mParams.gravity = Gravity.LEFT | Gravity.TOP; } /** * Make a standard toast that contains a custom view. * @param context * @param view * @return */ public static FloatToast makeView(final Context context, View view) { final FloatToast toast = new FloatToast(context); // record view toast.mNextView = view; // set touch listener. Implement drag effect view.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: { // if action down, record the press position toast.startX = event.getRawX(); toast.startY = event.getRawY(); break; } case MotionEvent.ACTION_MOVE: { // move , record current position float endX = event.getRawX(); float endY = event.getRawY(); // move toast toast.mParams.x += (int)(endX - toast.startX); toast.mParams.y += (int)(endY - toast.startY); // update start position toast.startX = endX; toast.startY = endY; // update view toast.mWM.updateViewLayout(toast.mView, toast.mParams); break; } case MotionEvent.ACTION_UP: { // get screen size Point point = new Point(); toast.mWM.getDefaultDisplay().getSize(point); // prevent toast.mParams.x y beyond the screen if(toast.mParams.x < 0) { toast.mParams.x = 0; } else if (toast.mParams.x + toast.mView.getWidth() > point.x) { toast.mParams.x = point.x - toast.mView.getWidth(); } if(toast.mParams.y < 0) { toast.mParams.y = 0; } else if (toast.mParams.y + toast.mView.getHeight() > point.y) { toast.mParams.y = point.y - toast.mView.getHeight(); } // record x y ConfigUtils.putInt(context, Constant.KEY_FLOAT_TOAST_X, toast.mParams.x); ConfigUtils.putInt(context, Constant.KEY_FLOAT_TOAST_Y, toast.mParams.y); break; } } return true; } }); return toast; } /** * you can use this method to get the view to change content * @return the view which was set on makeView */ public View getView() { return mNextView; } /** * Show the view in front of screen at last position until call close() * If the first show, it will show in center of screen. * You also can call show(x, y) to specific position. */ public void show() { if (mView != mNextView) { // remove the old view if necessary close(); mView = mNextView; Context context = mView.getContext().getApplicationContext(); if (context == null) { context = mView.getContext(); } mWM = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE); if (mView.getParent() != null) { mWM.removeView(mView); } // get device width and height Point point = new Point(); mWM.getDefaultDisplay().getSize(point); // get config x,y mParams.x = ConfigUtils.getInt(context, Constant.KEY_FLOAT_TOAST_X, (point.x - mView.getWidth()) / 2); mParams.y = ConfigUtils.getInt(context, Constant.KEY_FLOAT_TOAST_Y, (point.y - mView.getHeight()) / 2); // System.out.println("config (x, y) = " + mParams.x + "," + mParams.y); mWM.addView(mView, mParams); } } /** * Show the view in front of screen at position x,y until call close() * @param x the position x of view * @param y the position y of view */ public void show(int x, int y) { mParams.x = x; mParams.y = y; if (mView != mNextView) { // remove the old view if necessary close(); mView = mNextView; Context context = mView.getContext().getApplicationContext(); if (context == null) { context = mView.getContext(); } mWM = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE); if (mView.getParent() != null) { mWM.removeView(mView); } mWM.addView(mView, mParams); } } /** * hide the float toast and release resources */ public void close() { if (mView != null) { // note: checking parent() just to make sure the view has // been added... i have seen cases where we get here when // the view isn't yet added, so let's try not to crash. if (mView.getParent() != null) { mWM.removeView(mView); } mView = null; } } } /* * Make a standard toast that just contains a text view. * * @param context The context to use. Usually your {@link android.app.Application} * or {@link android.app.Activity} object. * @param text The text to show. Can be formatted text. * @param duration How long to display the message. Either {@link #LENGTH_SHORT} or * {@link #LENGTH_LONG} public static Toast makeText(Context context, CharSequence text, @Duration int duration) { Toast result = new Toast(context); LayoutInflater inflate = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); View v = inflate.inflate(com.android.internal.R.layout.transient_notification, null); TextView tv = (TextView)v.findViewById(com.android.internal.R.id.message); tv.setText(text); result.mNextView = v; result.mDuration = duration; return result; } * Show the view for the specified duration. public void show() { if (mNextView == null) { throw new RuntimeException("setView must have been called"); } INotificationManager service = getService(); String pkg = mContext.getOpPackageName(); TN tn = mTN; tn.mNextView = mNextView; try { service.enqueueToast(pkg, tn, mDuration); } catch (RemoteException e) { // Empty } } // TN 类继承了ITransientNotification.Stub,说明这个类提供了显示Toast的方法。 // TN类主要的代码 private final WindowManager.LayoutParams mParams = new WindowManager.LayoutParams(); final Handler mHandler = new Handler(); WindowManager mWM; TN() { // XXX This should be changed to use a Dialog, with a Theme.Toast // defined that sets up the layout params appropriately. final WindowManager.LayoutParams params = mParams; params.height = WindowManager.LayoutParams.WRAP_CONTENT; params.width = WindowManager.LayoutParams.WRAP_CONTENT; params.format = PixelFormat.TRANSLUCENT; params.windowAnimations = com.android.internal.R.style.Animation_Toast; params.type = WindowManager.LayoutParams.TYPE_TOAST; params.setTitle("Toast"); params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; } public void handleShow() { if (localLOGV) Log.v(TAG, "HANDLE SHOW: " + this + " mView=" + mView + " mNextView=" + mNextView); if (mView != mNextView) { // remove the old view if necessary handleHide(); mView = mNextView; Context context = mView.getContext().getApplicationContext(); String packageName = mView.getContext().getOpPackageName(); if (context == null) { context = mView.getContext(); } mWM = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE); // We can resolve the Gravity here by using the Locale for getting // the layout direction final Configuration config = mView.getContext().getResources().getConfiguration(); final int gravity = Gravity.getAbsoluteGravity(mGravity, config.getLayoutDirection()); mParams.gravity = gravity; if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) { mParams.horizontalWeight = 1.0f; } if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) { mParams.verticalWeight = 1.0f; } mParams.x = mX; mParams.y = mY; mParams.verticalMargin = mVerticalMargin; mParams.horizontalMargin = mHorizontalMargin; mParams.packageName = packageName; if (mView.getParent() != null) { if (localLOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this); mWM.removeView(mView); } if (localLOGV) Log.v(TAG, "ADD! " + mView + " in " + this); mWM.addView(mView, mParams); trySendAccessibilityEvent(); } } public void handleHide() { if (localLOGV) Log.v(TAG, "HANDLE HIDE: " + this + " mView=" + mView); if (mView != null) { // note: checking parent() just to make sure the view has // been added... i have seen cases where we get here when // the view isn't yet added, so let's try not to crash. if (mView.getParent() != null) { if (localLOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this); mWM.removeView(mView); } mView = null; } } * schedule handleShow into the right thread @Override public void show() { if (localLOGV) Log.v(TAG, "SHOW: " + this); mHandler.post(mShow); } * schedule handleHide into the right thread @Override public void hide() { if (localLOGV) Log.v(TAG, "HIDE: " + this); mHandler.post(mHide); } */