Android AIDL 实现两个APP之间的跨进程通信实例
1 Service端创建
首先需要创建一个Android工程然后创建AIDL文件,创建AIDL文件主要为了生成继承了Binder的Stub类,以便应用Binder进行进程间通信
servier端结构如下
AIDL代码如下
// IBookManager.aidl package com.example.bookserver.aidl; // Declare any non-default types here with import statements import com.example.bookserver.aidl.Book; interface IBookManager { /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString); List<Book> getBook(); boolean addBook(in Book book); }
之后创建一个实现了Parcelable的Book.java类用来传递数据
package com.example.bookserver.aidl; import android.os.Parcel; import android.os.Parcelable; /** * Created by SAMSUNG on 2016-09-07. */ public class Book implements Parcelable { private int id; private String name ; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Book{" + "id=" + id + ", name='" + name + '\'' + '}'; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(this.id); dest.writeString(this.name); } public Book() { } protected Book(Parcel in) { this.id = in.readInt(); this.name = in.readString(); } public static final Parcelable.Creator<Book> CREATOR = new Parcelable.Creator<Book>() { @Override public Book createFromParcel(Parcel source) { return new Book(source); } @Override public Book[] newArray(int size) { return new Book[size]; } }; }最后我们来写一个Service用于客户端绑定
package com.example.bookserver.service; import android.app.Service; import android.content.Intent; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import com.example.bookserver.aidl.Book; import com.example.bookserver.aidl.IBookManager; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; public class BookService extends Service { private CopyOnWriteArrayList<Book> boookList = new CopyOnWriteArrayList<Book>(); public BookService() { } Binder binder = new IBookManager.Stub(){ @Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException { } @Override public List<Book> getBook() throws RemoteException { return boookList; } @Override public boolean addBook(Book book) throws RemoteException { return boookList.add(book); } }; @Override public IBinder onBind(Intent intent) { return binder; } @Override public void onCreate() { super.onCreate(); Book book = new Book(); book.setId(12345); book.setName("Book 1"); boookList.add(book); } }
这样Server端就搞定了,接下来就该进行Client端的代码编写了
2 Client端
Client端结构如下
首先我们要讲AndroidStudio 通过AIDL生成的Binder导入到Client中并将Book.java也导入到Client中
然后写进行Service的绑定
package com.example.bookclient; import android.app.Service; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.os.IBinder; import android.util.Log; import com.example.bookserver.aidl.IBookManager; import java.util.List; /** * Created by SAMSUNG on 2016-09-07. */ public class BookServiceManager { Context mContext = null; IBookManager mService = null; private static BookServiceManager bsm ; public static BookServiceManager getInstance(Context context){ if(bsm==null){ bsm = new BookServiceManager(context); } return bsm; } public IBookManager getBookServie(){ if(mService==null){ Log.d("BookServiceManager", "getBookServie: "); this.connectService(); } return mService; } public BookServiceManager(Context mContext) { this.mContext = mContext; } ServiceConnection scc = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { Log.d("BookServiceManager", "getBookServie: 2 ==> Bind "); mService = IBookManager.Stub.asInterface(service); } @Override public void onServiceDisconnected(ComponentName name) { mService = null; } }; public boolean connectService(){ if(mService == null){ Log.d("BookServiceManager", "getBookServie: 2"); Intent intent = new Intent("com.example.bookserver.service.BookService"); final Intent eintent = new Intent(createExplicitFromImplicitIntent(mContext,intent)); mContext.bindService(eintent,scc, Service.BIND_AUTO_CREATE); } return true; } public static Intent createExplicitFromImplicitIntent(Context context, Intent implicitIntent) { // Retrieve all services that can match the given intent PackageManager pm = context.getPackageManager(); List<ResolveInfo> resolveInfo = pm.queryIntentServices(implicitIntent, 0); // Make sure only one match was found if (resolveInfo == null || resolveInfo.size() != 1) { return null; } // Get component info and create ComponentName ResolveInfo serviceInfo = resolveInfo.get(0); String packageName = serviceInfo.serviceInfo.packageName; String className = serviceInfo.serviceInfo.name; ComponentName component = new ComponentName(packageName, className); // Create a new intent. Use the old one for extras and such reuse Intent explicitIntent = new Intent(implicitIntent); // Set the component to be explicit explicitIntent.setComponent(component); return explicitIntent; } }
最后对设置Button进行调用
package com.example.bookclient; import android.os.Bundle; import android.os.RemoteException; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.View; import android.widget.Button; import com.example.bookserver.aidl.Book; import com.example.bookserver.aidl.IBookManager; public class MainActivity extends AppCompatActivity { IBookManager mBookService ; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button button = (Button) findViewById(R.id.button); Button addButton = (Button) findViewById(R.id.button3); Button findButton = (Button) findViewById(R.id.button2); BookServiceManager.getInstance(getApplication()).connectService(); button.setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View v) { mBookService = BookServiceManager.getInstance(getApplication()).getBookServie(); } }); addButton.setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View v) { Book book = new Book(); book.setId(2345); book.setName("add book!!"); try { mBookService.addBook(book); } catch (RemoteException e) { e.printStackTrace(); } } }); findButton.setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View v) { try { Log.d("MainActivity", mBookService.getBook().toString()); } catch (RemoteException e) { e.printStackTrace(); } } }); } }
1)截屏接口、2)静默安装接口、3)按键控制接口、4)关机/重启控制接口、5)关闭应用接口、
6)未知来源开关接口、7)控制应用使用接口、8)控制推送通知接口、9)控制SD/TF卡接口、10)控制USB口接口、
11) 网址/IP白名单接口、12)APK安装白名单接口、13)应用卸载接口、14)护眼模式开关接口、15)应用联网控制接口、
16)启动Service或Activity接口、17)控制无障碍服务接口、18)控制设备管理器接口、19)控制访问使用记录接口、
20)联系人白名单接口、21)获取联系人白名单接口
package com.prize.txInterface.util; import com.prize.txInterface.application.TXApplication; import android.util.Log; public class TXLog { public static int d(String tag, String msg) { if (TXApplication.DEBUG) { return Log.d(tag, msg+getFunctionName()); } else { return 0; } } public static int i(String tag, String msg) { if (TXApplication.DEBUG) { return Log.d(tag, msg+getFunctionName()); } else { return 0; } } public static int e(String tag, String msg) { if (TXApplication.DEBUG) { return Log.e(tag, msg+getFunctionName()); } else { return 0; } } /** * 方便日志定位 * add by mafei * @param tag 标签 * @return */ private static String getFunctionName() { StackTraceElement[] sts = Thread.currentThread().getStackTrace(); if(sts == null) { return null; } for(StackTraceElement st : sts) { if(st.isNativeMethod()) { continue; } if(st.getClassName().equals(Thread.class.getName())) { continue; } if(st.getClassName().equals(TXLog.class.getName())) { continue; } return "[" + Thread.currentThread().getName() + ": " + st.getFileName() + ":" + st.getLineNumber() + " " + st.getMethodName() + " ]"; } return null; } }
package com.prize.txInterface.provider; import com.prize.txInterface.util.TXLog; import android.content.ContentProvider; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; import android.content.UriMatcher; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.net.Uri; public class PrizeWhiteListProvider extends ContentProvider{ public static final String TAG = "snail_PrizeWhiteListProvider"; public static final String DB_NAME = "whitelist.db"; public static final int DB_VESION = 1; public static Uri CONTENT_URI_INSTALL = null; public static Uri CONTENT_URI_IP = null; public static Uri CONTENT_URI_CONTACT = null; private static UriMatcher sUriMatcher; public static final String TABLE_NAME_INSTALL = "install"; public static final String TABLE_NAME_IP = "ip"; public static final String TABLE_NAME_CONTACT = "contact"; private static final int _INSTALL = 1; private static final int _IP = 2; private static final int _CONTACT = 3; private ContentResolver mContentResolver; static DatabaseHelper mOpenHelper; static SQLiteDatabase db; public static SQLiteDatabase getDbInstance(Context context){ TXLog.i(TAG, "--whitelist.db-----onCreate----getDbInstance---- "); if(db==null) { mOpenHelper = new DatabaseHelper(context); db = mOpenHelper.getReadableDatabase(); } return db; } @Override public boolean onCreate() { // TODO Auto-generated method stub TXLog.i(TAG, "--whitelist.db-----onCreate-- "); String db_auth = getContext().getPackageName() + ".provider"; CONTENT_URI_INSTALL = Uri.parse("content://" + db_auth + "/"+ TABLE_NAME_INSTALL); CONTENT_URI_IP = Uri.parse("content://" + db_auth + "/"+ TABLE_NAME_IP); CONTENT_URI_CONTACT = Uri.parse("content://" + db_auth + "/"+ TABLE_NAME_CONTACT); sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); sUriMatcher.addURI(db_auth, TABLE_NAME_INSTALL, _INSTALL); sUriMatcher.addURI(db_auth, TABLE_NAME_IP, _IP); sUriMatcher.addURI(db_auth, TABLE_NAME_CONTACT, _CONTACT); getDbInstance(this.getContext()); mContentResolver = getContext().getContentResolver(); return true; } @Override public Cursor query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder) { // TODO Auto-generated method stub Cursor cur = null; String tableName = getTableName(uri); TXLog.i(TAG, "--whitelist.db-----query tableName = " + tableName+ " selection = " + selection); cur = db.query(tableName, projection, selection, selectionArgs, null,null, sortOrder); return cur; } @Override public String getType(Uri uri) { // TODO Auto-generated method stub throw new IllegalArgumentException("Uri IllegalArgument:" + uri); } @Override public Uri insert(Uri uri, ContentValues values) { // TODO Auto-generated method stub long id = -1; String tableName = getTableName(uri); TXLog.i(TAG, "--whitelist.db-----insert tableName = " + tableName); id = db.insert(tableName, "Content is empty", values); if (id == -1) { return null; } return Uri.parse("" + id); } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { // TODO Auto-generated method stub int num = 0; String tableName = getTableName(uri); TXLog.i(TAG, "--whitelist.db-----delete tableName = " + tableName+ "selection = " + selection); num = db.delete(tableName, selection, selectionArgs); return num; } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { // TODO Auto-generated method stub int num = 0; String tableName = getTableName(uri); TXLog.i(TAG, "--whitelist.db-----update tableName = " + tableName+ "selection = " + selection); num = db.update(tableName, values, selection, selectionArgs); return num; } public String getTableName(Uri uri) { switch (sUriMatcher.match(uri)) { case _INSTALL:// return TABLE_NAME_INSTALL; case _IP:// return TABLE_NAME_IP; case _CONTACT:// return TABLE_NAME_CONTACT; default: return null; } } public static class DatabaseHelper extends SQLiteOpenHelper { public DatabaseHelper(Context context) { super(context, DB_NAME, null, DB_VESION); } private static final String SQL_CREATE_TABLE_INSTALL = "CREATE TABLE IF NOT EXISTS " + TABLE_NAME_INSTALL + " (" + "id" + " INTEGER PRIMARY KEY AUTOINCREMENT," + "package" + " TEXT," + "isActive"+ " TEXT" + ");"; private static final String SQL_CREATE_TABLE_IP = "CREATE TABLE IF NOT EXISTS " + TABLE_NAME_IP + " (" + "id" + " INTEGER PRIMARY KEY AUTOINCREMENT," + "host" + " TEXT," + "isActive" + " TEXT"+ ");"; private static final String SQL_CREATE_TABLE_CONTACT = "CREATE TABLE IF NOT EXISTS " + TABLE_NAME_CONTACT + " (" + "id" + " INTEGER PRIMARY KEY AUTOINCREMENT," + "phonenum" + " INTEGER," + "isActive" + " TEXT"+ ");"; @Override public void onCreate(SQLiteDatabase db) { String sql_install = SQL_CREATE_TABLE_INSTALL; db.execSQL(sql_install); String sql_ip = SQL_CREATE_TABLE_IP; db.execSQL(sql_ip); String sql_contact = SQL_CREATE_TABLE_CONTACT; db.execSQL(sql_contact); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { TXLog.i(TAG, "--whitelist.db------onUpgrade oldVersion = " + oldVersion+ "newVersion = " + newVersion); onCreate(db); } } }
public static String takeScreenshot(final Context context,String path_name) { mContext = context; final String mImageFileName; synchronized (mScreenshotLock) { if (mScreenshotConnection != null) { return ""; } Intent intent = new Intent(); intent.setPackage("com.prize.txInterface"); if(TextUtils.isEmpty(path_name)){ long mImageTime = System.currentTimeMillis(); String imageDate = new SimpleDateFormat("yyyyMMdd-HHmmss").format(new Date(mImageTime)); String default_dir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).getAbsolutePath()+File.separator +"Screenshots/"; mImageFileName = default_dir+String.format("Screenshot_%s", imageDate); TXLog.d(TAG, "----takeScreenshot-------path_name==null-------mImageFileName=="+mImageFileName); }else { mImageFileName =path_name; TXLog.d(TAG, "----takeScreenshot---path_name!!!!==null------------mImageFileName=="+mImageFileName); } intent.setClass(context, TakeScreenshotService.class); ServiceConnection conn = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { synchronized (mScreenshotLock) { if (mScreenshotConnection != this) { return; } TXLog.d(TAG, "----takeScreenshot--------onServiceConnected------ComponentName=="+name.toString()); Messenger messenger = new Messenger(service); Message msg = Message.obtain(null, 1); final ServiceConnection myConn = this; Handler h = new Handler(TXApplication.getInstance().getHandler().getLooper()) { @Override public void handleMessage(Message msg) { synchronized (mScreenshotLock) { if (mScreenshotConnection == myConn) { TXLog.d(TAG,"---Handler---ServiceConnection--------------handleMessage----------------"); Intent intent = new Intent(); intent.setAction("screenshot.path"); intent.putExtra("path", mImageFileName); context.sendBroadcast(intent); context.unbindService(mScreenshotConnection); mScreenshotConnection = null; TXApplication.getInstance().getHandler().removeCallbacks(mScreenshotTimeout); } } } }; msg.replyTo = new Messenger(h); msg.arg1 = msg.arg2 = 0; msg.obj =mImageFileName; try { TXLog.d(TAG,"------ServiceConnection---------------messenger.send-----------------"); messenger.send(msg); } catch (RemoteException e) { } } } @Override public void onServiceDisconnected(ComponentName name) { } }; TXLog.d(TAG, "-----takeScreenshot----bindServiceAsUser-----------"); if (context.bindService(intent, conn, Context.BIND_AUTO_CREATE)) { TXLog.d(TAG, "----takeScreenshot---bindServiceAsUser-----start------"); mScreenshotConnection = conn; TXApplication.getInstance().getHandler().postDelayed(mScreenshotTimeout, 10000); }else { TXLog.d(TAG, "-----takeScreenshot----bindServiceAsUser---else--------"); } } TXLog.d(TAG, "-----takeScreenshot--------return=="+path_name); return mImageFileName; } final static Runnable mScreenshotTimeout = new Runnable() { @Override public void run() { synchronized (mScreenshotLock) { if (mScreenshotConnection != null) { TXLog.d(TAG, "----takeScreenshot--------mScreenshotTimeout------unbindService-------------"); if(mContext != null){ mContext.unbindService(mScreenshotConnection); } mScreenshotConnection = null; } } } }; /* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.prize.txInterface.screenshot; import com.prize.txInterface.util.TXLog; import android.app.Service; import android.content.Intent; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.Messenger; import android.os.RemoteException; public class TakeScreenshotService extends Service { private static final String TAG = "snail_TakeScreenshotService"; private static GlobalScreenshot mScreenshot; private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { TXLog.d(TAG, "---------------onBind--handleMessage-----"); switch (msg.what) { case 1: final Messenger callback = msg.replyTo; if (mScreenshot == null) { mScreenshot = new GlobalScreenshot(TakeScreenshotService.this); } mScreenshot.takeScreenshot(new Runnable() { @Override public void run() { Message reply = Message.obtain(null, 1); try { callback.send(reply); } catch (RemoteException e) { } } }, msg.arg1 > 0, msg.arg2 > 0,(String) msg.obj); } } }; @Override public IBinder onBind(Intent intent) { TXLog.d(TAG, "---------------onBind-------"); return new Messenger(mHandler).getBinder(); } } /* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.prize.txInterface.screenshot; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ValueAnimator; import android.animation.ValueAnimator.AnimatorUpdateListener; import android.app.Notification; import android.app.Notification.BigPictureStyle; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.ColorMatrix; import android.graphics.ColorMatrixColorFilter; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.PointF; import android.media.MediaActionSound; import android.net.Uri; import android.os.AsyncTask; import android.os.Environment; import android.os.Process; import android.os.IBinder; import android.provider.MediaStore; import android.util.DisplayMetrics; import android.view.Display; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.Surface; import android.view.SurfaceControl; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; import android.view.animation.Interpolator; import android.widget.ImageView; import java.io.File; import java.io.FileOutputStream; import java.io.OutputStream; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import android.content.ComponentName; import android.util.Log; import com.mediatek.storage.StorageManagerEx; import com.prize.txInterface.R; import com.prize.txInterface.R.string; import com.prize.txInterface.util.TXLog; /** * POD used in the AsyncTask which saves an image in the background. */ class SaveImageInBackgroundData { Context context; Bitmap image; Uri imageUri; Runnable finisher; String mImagePath; int iconSize; int result; int previewWidth; int previewheight; void clearImage() { image = null; imageUri = null; iconSize = 0; } void clearContext() { context = null; } } /** * An AsyncTask that saves an image to the media store in the background. */ class SaveImageInBackgroundTask extends AsyncTask<SaveImageInBackgroundData, Void, SaveImageInBackgroundData> { private static final String TAG = "snail_SaveImageInBackgroundTask"; private static final String SCREENSHOTS_DIR_NAME = "Screenshots"; private static final String SCREENSHOT_FILE_NAME_TEMPLATE = "Screenshot_%s.png"; private static final String SCREENSHOT_SHARE_SUBJECT_TEMPLATE = "Screenshot (%s)"; private final int mNotificationId; private final NotificationManager mNotificationManager; private final Notification.Builder mNotificationBuilder, mPublicNotificationBuilder; private File mScreenshotDir; private final String mImageFileName; private final String mImageFilePath; private final long mImageTime; private final BigPictureStyle mNotificationStyle; private final int mImageWidth; private final int mImageHeight; // WORKAROUND: We want the same notification across screenshots that we update so that we don't // spam a user's notification drawer. However, we only show the ticker for the saving state // and if the ticker text is the same as the previous notification, then it will not show. So // for now, we just add and remove a space from the ticker text to trigger the animation when // necessary. private static boolean mTickerAddSpace; SaveImageInBackgroundTask(Context context, SaveImageInBackgroundData data,NotificationManager nManager, int nId) { Resources r = context.getResources(); // Prepare all the output metadata File file = new File(StorageManagerEx.getDefaultPath(),Environment.DIRECTORY_PICTURES); try{ file.mkdirs(); if(file.exists()){ mScreenshotDir = new File(file.toString() , SCREENSHOTS_DIR_NAME); } else { mScreenshotDir = new File(Environment.getExternalStoragePublicDirectory( Environment.DIRECTORY_PICTURES), SCREENSHOTS_DIR_NAME); } }catch (Exception e) { Log.d(TAG, "save-Exception:"+e.toString()); } TXLog.d(TAG,"SaveImageInBackgroundTask mScreenshotDir "+mScreenshotDir); mImageTime = System.currentTimeMillis(); mImageFilePath = data.mImagePath; String picdir = new File(mImageFilePath).getParent(); File rootdirFile = new File(picdir); if(!rootdirFile.exists()){ rootdirFile.mkdirs(); } mImageFileName = mImageFilePath.substring(mImageFilePath.lastIndexOf("/") + 1,mImageFilePath.lastIndexOf(".")); TXLog.d(TAG,"----------SaveImageInBackgroundTask---mImageFilePath="+mImageFilePath+" picdir=="+picdir+" mImageFileName=="+mImageFileName); // Create the large notification icon mImageWidth = data.image.getWidth(); mImageHeight = data.image.getHeight(); int iconSize = data.iconSize; int previewWidth = data.previewWidth; int previewHeight = data.previewheight; Canvas c = new Canvas(); Paint paint = new Paint(); ColorMatrix desat = new ColorMatrix(); /*Prize scale the screenshot picture with no scrim color liyao 20150708 start*/ // desat.setSaturation(0.25f); paint.setColorFilter(new ColorMatrixColorFilter(desat)); Matrix matrix = new Matrix(); int overlayColor = 0x40FFFFFF; Bitmap picture = Bitmap.createBitmap(previewWidth, previewHeight, data.image.getConfig()); matrix.setTranslate((previewWidth - mImageWidth) / 2, (previewHeight - mImageHeight) / 2); c.setBitmap(picture); c.drawBitmap(data.image, matrix, paint); //c.drawColor(0x40FFFFFF); /*Prize scale the screenshot picture with no scrim color liyao 20150708 end*/ c.setBitmap(null); // Note, we can't use the preview for the small icon, since it is non-square float scale = (float) iconSize / Math.min(mImageWidth, mImageHeight); Bitmap icon = Bitmap.createBitmap(iconSize, iconSize, data.image.getConfig()); matrix.setScale(scale, scale); matrix.postTranslate((iconSize - (scale * mImageWidth)) / 2, (iconSize - (scale * mImageHeight)) / 2); c.setBitmap(icon); c.drawBitmap(data.image, matrix, paint); c.drawColor(overlayColor); c.setBitmap(null); // Show the intermediate notification mTickerAddSpace = !mTickerAddSpace; mNotificationId = nId; mNotificationManager = nManager; final long now = System.currentTimeMillis(); mNotificationBuilder = new Notification.Builder(context) .setTicker(r.getString(R.string.screenshot_saving_ticker) + (mTickerAddSpace ? " " : "")) .setContentTitle(r.getString(R.string.screenshot_saving_title)) .setContentText(r.getString(R.string.screenshot_saving_text)) /*PRIZE-screenshot notification icon- liufan-2016-07-14-start*/ .setSmallIcon(R.drawable.stat_notify_image_prize) /*PRIZE-screenshot notification icon- liufan-2016-07-14-end*/ .setWhen(now) .setColor(r.getColor(com.android.internal.R.color.system_notification_accent_color)); mNotificationStyle = new Notification.BigPictureStyle() .bigPicture(picture.createAshmemBitmap()); mNotificationBuilder.setStyle(mNotificationStyle); // For "public" situations we want to show all the same info but // omit the actual screenshot image. mPublicNotificationBuilder = new Notification.Builder(context) .setContentTitle(r.getString(R.string.screenshot_saving_title)) .setContentText(r.getString(R.string.screenshot_saving_text)) /*PRIZE-screenshot notification icon- liufan-2016-07-14-start*/ .setSmallIcon(R.drawable.stat_notify_image_prize) /*PRIZE-screenshot notification icon- liufan-2016-07-14-end*/ .setCategory(Notification.CATEGORY_PROGRESS) .setWhen(now) .setColor(r.getColor( com.android.internal.R.color.system_notification_accent_color)); /*PRIZE-don't change screenshot notification icon- liufan-2016-07-14-start*/ Notification pn = mPublicNotificationBuilder.build(); pn.flags |= Notification.FLAG_KEEP_NOTIFICATION_ICON; mNotificationBuilder.setPublicVersion(pn); /*PRIZE-don't change screenshot notification icon- liufan-2016-07-14-end*/ Notification n = mNotificationBuilder.build(); n.flags |= Notification.FLAG_NO_CLEAR; /*PRIZE-don't change screenshot notification icon- liufan-2016-07-14-start*/ n.flags |= Notification.FLAG_KEEP_NOTIFICATION_ICON; /*PRIZE-don't change screenshot notification icon- liufan-2016-07-14-end*/ mNotificationManager.notify(nId, n); // On the tablet, the large icon makes the notification appear as if it is clickable (and // on small devices, the large icon is not shown) so defer showing the large icon until // we compose the final post-save notification below. mNotificationBuilder.setLargeIcon(icon.createAshmemBitmap()); // But we still don't set it for the expanded view, allowing the smallIcon to show here. mNotificationStyle.bigLargeIcon((Bitmap) null); } @Override protected SaveImageInBackgroundData doInBackground(SaveImageInBackgroundData... params) { if (params.length != 1) return null; if (isCancelled()) { params[0].clearImage(); params[0].clearContext(); return null; } // By default, AsyncTask sets the worker thread to have background thread priority, so bump // it back up so that we save a little quicker. Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND); Context context = params[0].context; Bitmap image = params[0].image; Resources r = context.getResources(); try { // Create screenshot directory if it doesn't exist mScreenshotDir.mkdirs(); // media provider uses seconds for DATE_MODIFIED and DATE_ADDED, but milliseconds // for DATE_TAKEN long dateSeconds = mImageTime / 1000; // Save OutputStream out = new FileOutputStream(mImageFilePath); image.compress(Bitmap.CompressFormat.PNG, 100, out); out.flush(); out.close(); // Save the screenshot to the MediaStore ContentValues values = new ContentValues(); ContentResolver resolver = context.getContentResolver(); values.put(MediaStore.Images.ImageColumns.DATA, mImageFilePath); values.put(MediaStore.Images.ImageColumns.TITLE, mImageFileName); values.put(MediaStore.Images.ImageColumns.DISPLAY_NAME, mImageFileName); values.put(MediaStore.Images.ImageColumns.DATE_ADDED, dateSeconds); values.put(MediaStore.Images.ImageColumns.DATE_MODIFIED, dateSeconds); values.put(MediaStore.Images.ImageColumns.MIME_TYPE, "image/png"); values.put(MediaStore.Images.ImageColumns.WIDTH, mImageWidth); values.put(MediaStore.Images.ImageColumns.HEIGHT, mImageHeight); values.put(MediaStore.Images.ImageColumns.SIZE, new File(mImageFilePath).length()); Uri uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); // Create a share intent String subjectDate = DateFormat.getDateTimeInstance().format(new Date(mImageTime)); String subject = String.format(SCREENSHOT_SHARE_SUBJECT_TEMPLATE, subjectDate); Intent sharingIntent = new Intent(Intent.ACTION_SEND); sharingIntent.setType("image/png"); sharingIntent.putExtra(Intent.EXTRA_STREAM, uri); sharingIntent.putExtra(Intent.EXTRA_SUBJECT, subject); // Create a share action for the notification final PendingIntent callback = PendingIntent.getBroadcast(context, 0, new Intent(context, GlobalScreenshot.TargetChosenReceiver.class) .putExtra(GlobalScreenshot.CANCEL_ID, mNotificationId), PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT); Intent chooserIntent = Intent.createChooser(sharingIntent, null, callback.getIntentSender()); chooserIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK); mNotificationBuilder.addAction(R.drawable.ic_screenshot_share_prize, r.getString(com.android.internal.R.string.share), PendingIntent.getActivity(context, 0, chooserIntent, PendingIntent.FLAG_CANCEL_CURRENT)); // Create a delete action for the notification final PendingIntent deleteAction = PendingIntent.getBroadcast(context, 0, new Intent(context, GlobalScreenshot.DeleteScreenshotReceiver.class) .putExtra(GlobalScreenshot.CANCEL_ID, mNotificationId) .putExtra(GlobalScreenshot.SCREENSHOT_URI_ID, uri.toString()), PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT); mNotificationBuilder.addAction(R.drawable.ic_screenshot_delete_prize, r.getString(com.android.internal.R.string.delete), deleteAction); params[0].imageUri = uri; params[0].image = null; params[0].result = 0; } catch (Exception e) { // IOException/UnsupportedOperationException may be thrown if external storage is not // mounted params[0].clearImage(); params[0].result = 1; } // Recycle the bitmap data if (image != null) { image.recycle(); } return params[0]; } @Override protected void onPostExecute(SaveImageInBackgroundData params) { if (isCancelled()) { params.finisher.run(); params.clearImage(); params.clearContext(); return; } if (params.result > 0) { // Show a message that we've failed to save the image to disk GlobalScreenshot.notifyScreenshotError(params.context, mNotificationManager); } else { // Show the final notification to indicate screenshot saved Resources r = params.context.getResources(); // Create the intent to show the screenshot in gallery /*PRIZE-update the open picture method- liufan-2015-05-23-start*/ Intent launchIntent = new Intent(Intent.ACTION_VIEW); launchIntent.setDataAndType(params.imageUri, "image/png"); //launchIntent = new Intent(Intent.ACTION_VIEW) // .setType("image/png") // .putExtra(Intent.EXTRA_STREAM, params.imageUri) // .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); try{ ComponentName cn = new ComponentName("com.android.gallery3d","com.android.gallery3d.app.GalleryActivity"); launchIntent.setComponent(cn); }catch (Exception e){ } /*PRIZE-update the open picture method- liufan-2015-05-23-end*/ launchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); final long now = System.currentTimeMillis(); mNotificationBuilder .setContentTitle(r.getString(R.string.screenshot_saved_title)) .setContentText(r.getString(R.string.screenshot_saved_text)) .setContentIntent(PendingIntent.getActivity(params.context, 0, launchIntent, 0)) .setWhen(now) .setAutoCancel(true) .setColor(r.getColor( com.android.internal.R.color.system_notification_accent_color));; // Update the text in the public version as well mPublicNotificationBuilder .setContentTitle(r.getString(R.string.screenshot_saved_title)) .setContentText(r.getString(R.string.screenshot_saved_text)) .setContentIntent(PendingIntent.getActivity(params.context, 0, launchIntent, 0)) .setWhen(now) .setAutoCancel(true) .setColor(r.getColor( com.android.internal.R.color.system_notification_accent_color)); /*PRIZE-don't change screenshot notification icon- liufan-2016-07-14-start*/ Notification pn = mPublicNotificationBuilder.build(); pn.flags |= Notification.FLAG_KEEP_NOTIFICATION_ICON; mNotificationBuilder.setPublicVersion(pn); /*PRIZE-don't change screenshot notification icon- liufan-2016-07-14-end*/ Notification n = mNotificationBuilder.build(); n.flags &= ~Notification.FLAG_NO_CLEAR; /*PRIZE-don't change screenshot notification icon- liufan-2016-07-14-start*/ n.flags |= Notification.FLAG_KEEP_NOTIFICATION_ICON; /*PRIZE-don't change screenshot notification icon- liufan-2016-07-14-end*/ mNotificationManager.notify(mNotificationId, n); TXLog.d(TAG,"------onPostExecute--------------------------------"); } params.finisher.run(); params.clearContext(); } } /** * An AsyncTask that deletes an image from the media store in the background. */ class DeleteImageInBackgroundTask extends AsyncTask<Uri, Void, Void> { private static final String TAG = "DeleteImageInBackgroundTask"; private Context mContext; DeleteImageInBackgroundTask(Context context) { mContext = context; } @Override protected Void doInBackground(Uri... params) { if (params.length != 1) return null; Uri screenshotUri = params[0]; ContentResolver resolver = mContext.getContentResolver(); resolver.delete(screenshotUri, null, null); return null; } } /** * TODO: * - Performance when over gl surfaces? Ie. Gallery * - what do we say in the Toast? Which icon do we get if the user uses another * type of gallery? */ class GlobalScreenshot { private static final String TAG = "GlobalScreenshot"; static final String CANCEL_ID = "android:cancel_id"; static final String SCREENSHOT_URI_ID = "android:screenshot_uri_id"; private static final int SCREENSHOT_FLASH_TO_PEAK_DURATION = 130; private static final int SCREENSHOT_DROP_IN_DURATION = 430; private static final int SCREENSHOT_DROP_OUT_DELAY = 500; private static final int SCREENSHOT_DROP_OUT_DURATION = 430; private static final int SCREENSHOT_DROP_OUT_SCALE_DURATION = 370; private static final int SCREENSHOT_FAST_DROP_OUT_DURATION = 320; private static final float BACKGROUND_ALPHA = 0.5f; private static final float SCREENSHOT_SCALE = 1f; private static final float SCREENSHOT_DROP_IN_MIN_SCALE = SCREENSHOT_SCALE * 0.725f; private static final float SCREENSHOT_DROP_OUT_MIN_SCALE = SCREENSHOT_SCALE * 0.45f; private static final float SCREENSHOT_FAST_DROP_OUT_MIN_SCALE = SCREENSHOT_SCALE * 0.6f; private static final float SCREENSHOT_DROP_OUT_MIN_SCALE_OFFSET = 0f; private final int mPreviewWidth; private final int mPreviewHeight; private Context mContext; private WindowManager mWindowManager; private WindowManager.LayoutParams mWindowLayoutParams; private NotificationManager mNotificationManager; private Display mDisplay; private DisplayMetrics mDisplayMetrics; private Matrix mDisplayMatrix; private Bitmap mScreenBitmap; private View mScreenshotLayout; private ImageView mBackgroundView; private ImageView mScreenshotView; private ImageView mScreenshotFlash; private AnimatorSet mScreenshotAnimation; private int mNotificationIconSize; private float mBgPadding; private float mBgPaddingScale; private AsyncTask<SaveImageInBackgroundData, Void, SaveImageInBackgroundData> mSaveInBgTask; private MediaActionSound mCameraSound; /** * @param context everything needs a context :( */ public GlobalScreenshot(Context context) { TXLog.d(TAG, "----GlobalScreenshot------start------"); Resources r = context.getResources(); mContext = context; LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); // Inflate the screenshot layout mDisplayMatrix = new Matrix(); mScreenshotLayout = layoutInflater.inflate(R.layout.global_screenshot, null); mBackgroundView = (ImageView) mScreenshotLayout.findViewById(R.id.global_screenshot_background); mScreenshotView = (ImageView) mScreenshotLayout.findViewById(R.id.global_screenshot); mScreenshotFlash = (ImageView) mScreenshotLayout.findViewById(R.id.global_screenshot_flash); mScreenshotLayout.setFocusable(true); mScreenshotLayout.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { // Intercept and ignore all touch events return true; } }); // Setup the window that we are going to use mWindowLayoutParams = new WindowManager.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, 0, 0, WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY, WindowManager.LayoutParams.FLAG_FULLSCREEN | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED, PixelFormat.TRANSLUCENT); mWindowLayoutParams.setTitle("ScreenshotAnimation"); mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); mDisplay = mWindowManager.getDefaultDisplay(); mDisplayMetrics = new DisplayMetrics(); mDisplay.getRealMetrics(mDisplayMetrics); // Get the various target sizes mNotificationIconSize = r.getDimensionPixelSize(android.R.dimen.notification_large_icon_height); // Scale has to account for both sides of the bg mBgPadding = (float) r.getDimensionPixelSize(R.dimen.global_screenshot_bg_padding); mBgPaddingScale = mBgPadding / mDisplayMetrics.widthPixels; // determine the optimal preview size int panelWidth = 0; try { panelWidth = r.getDimensionPixelSize(R.dimen.notification_panel_width); } catch (Resources.NotFoundException e) { } if (panelWidth <= 0) { // includes notification_panel_width==match_parent (-1) panelWidth = mDisplayMetrics.widthPixels; } mPreviewWidth = panelWidth; mPreviewHeight = r.getDimensionPixelSize(R.dimen.notification_max_height); // Setup the Camera shutter sound mCameraSound = new MediaActionSound(); mCameraSound.load(MediaActionSound.SHUTTER_CLICK); } /** * Creates a new worker thread and saves the screenshot to the media store. */ private void saveScreenshotInWorkerThread(Runnable finisher,String path_name) { TXLog.d(TAG, "----saveScreenshotInWorkerThread------start------"); SaveImageInBackgroundData data = new SaveImageInBackgroundData(); data.context = mContext; data.image = mScreenBitmap; data.iconSize = mNotificationIconSize; data.finisher = finisher; data.previewWidth = mPreviewWidth; data.previewheight = mPreviewHeight; data.mImagePath = path_name+".png"; if (mSaveInBgTask != null) { mSaveInBgTask.cancel(false); } mSaveInBgTask = new SaveImageInBackgroundTask(mContext, data, mNotificationManager, R.id.notification_screenshot).execute(data); } /** * @return the current display rotation in degrees */ private float getDegreesForRotation(int value) { switch (value) { case Surface.ROTATION_90: return 360f - 90f; case Surface.ROTATION_180: return 360f - 180f; case Surface.ROTATION_270: return 360f - 270f; } return 0f; } /** * Takes a screenshot of the current display and shows an animation. */ void takeScreenshot(Runnable finisher, boolean statusBarVisible, boolean navBarVisible,String path_name) { TXLog.d(TAG, "----takeScreenshot------start------"); // We need to orient the screenshot correctly (and the Surface api seems to take screenshots // only in the natural orientation of the device :!) mDisplay.getRealMetrics(mDisplayMetrics); float[] dims = {mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels}; float degrees = getDegreesForRotation(mDisplay.getRotation()); boolean requiresRotation = (degrees > 0); if (requiresRotation) { // Get the dimensions of the device in its native orientation mDisplayMatrix.reset(); mDisplayMatrix.preRotate(-degrees); mDisplayMatrix.mapPoints(dims); dims[0] = Math.abs(dims[0]); dims[1] = Math.abs(dims[1]); } // Take the screenshot mScreenBitmap = SurfaceControl.screenshot((int) dims[0], (int) dims[1]); if (mScreenBitmap == null) { notifyScreenshotError(mContext, mNotificationManager); finisher.run(); return; } if (requiresRotation) { // Rotate the screenshot to the current orientation Bitmap ss = Bitmap.createBitmap(mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels, Bitmap.Config.ARGB_8888); Canvas c = new Canvas(ss); c.translate(ss.getWidth() / 2, ss.getHeight() / 2); c.rotate(degrees); c.translate(-dims[0] / 2, -dims[1] / 2); c.drawBitmap(mScreenBitmap, 0, 0, null); c.setBitmap(null); // Recycle the previous bitmap mScreenBitmap.recycle(); mScreenBitmap = ss; } // Optimizations /* prize-xuchunming-20170710-add circle window at screen four corners-start */ //mScreenBitmap.setHasAlpha(false); /* prize-xuchunming-20170710-add circle window at screen four corners-end */ mScreenBitmap.prepareToDraw(); // Start the post-screenshot animation startAnimation(finisher, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels, statusBarVisible, navBarVisible,path_name); } /** * Starts the animation after taking the screenshot */ private void startAnimation(final Runnable finisher, int w, int h, boolean statusBarVisible, boolean navBarVisible,final String path_name) { // Add the view for the animation TXLog.d(TAG, "----startAnimation------start------"); mScreenshotView.setImageBitmap(mScreenBitmap); mScreenshotLayout.requestFocus(); // Setup the animation with the screenshot just taken if (mScreenshotAnimation != null) { mScreenshotAnimation.end(); mScreenshotAnimation.removeAllListeners(); } mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams); ValueAnimator screenshotDropInAnim = createScreenshotDropInAnimation(); ValueAnimator screenshotFadeOutAnim = createScreenshotDropOutAnimation(w, h, statusBarVisible, navBarVisible); mScreenshotAnimation = new AnimatorSet(); mScreenshotAnimation.playSequentially(screenshotDropInAnim, screenshotFadeOutAnim); mScreenshotAnimation.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { // Save the screenshot once we have a bit of time now saveScreenshotInWorkerThread(finisher,path_name); mWindowManager.removeView(mScreenshotLayout); // Clear any references to the bitmap mScreenBitmap = null; mScreenshotView.setImageBitmap(null); } }); mScreenshotLayout.post(new Runnable() { @Override public void run() { // Play the shutter sound to notify that we've taken a screenshot mCameraSound.play(MediaActionSound.SHUTTER_CLICK); mScreenshotView.setLayerType(View.LAYER_TYPE_HARDWARE, null); mScreenshotView.buildLayer(); mScreenshotAnimation.start(); } }); } private ValueAnimator createScreenshotDropInAnimation() { final float flashPeakDurationPct = ((float) (SCREENSHOT_FLASH_TO_PEAK_DURATION) / SCREENSHOT_DROP_IN_DURATION); final float flashDurationPct = 2f * flashPeakDurationPct; final Interpolator flashAlphaInterpolator = new Interpolator() { @Override public float getInterpolation(float x) { // Flash the flash view in and out quickly if (x <= flashDurationPct) { return (float) Math.sin(Math.PI * (x / flashDurationPct)); } return 0; } }; final Interpolator scaleInterpolator = new Interpolator() { @Override public float getInterpolation(float x) { // We start scaling when the flash is at it's peak if (x < flashPeakDurationPct) { return 0; } return (x - flashDurationPct) / (1f - flashDurationPct); } }; ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f); anim.setDuration(SCREENSHOT_DROP_IN_DURATION); anim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { mBackgroundView.setAlpha(0f); mBackgroundView.setVisibility(View.VISIBLE); mScreenshotView.setAlpha(0f); mScreenshotView.setTranslationX(0f); mScreenshotView.setTranslationY(0f); mScreenshotView.setScaleX(SCREENSHOT_SCALE + mBgPaddingScale); mScreenshotView.setScaleY(SCREENSHOT_SCALE + mBgPaddingScale); mScreenshotView.setVisibility(View.VISIBLE); mScreenshotFlash.setAlpha(0f); mScreenshotFlash.setVisibility(View.VISIBLE); } @Override public void onAnimationEnd(android.animation.Animator animation) { mScreenshotFlash.setVisibility(View.GONE); } }); anim.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float t = (Float) animation.getAnimatedValue(); float scaleT = (SCREENSHOT_SCALE + mBgPaddingScale) - scaleInterpolator.getInterpolation(t) * (SCREENSHOT_SCALE - SCREENSHOT_DROP_IN_MIN_SCALE); mBackgroundView.setAlpha(scaleInterpolator.getInterpolation(t) * BACKGROUND_ALPHA); mScreenshotView.setAlpha(t); mScreenshotView.setScaleX(scaleT); mScreenshotView.setScaleY(scaleT); mScreenshotFlash.setAlpha(flashAlphaInterpolator.getInterpolation(t)); } }); return anim; } private ValueAnimator createScreenshotDropOutAnimation(int w, int h, boolean statusBarVisible, boolean navBarVisible) { ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f); anim.setStartDelay(SCREENSHOT_DROP_OUT_DELAY); anim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { mBackgroundView.setVisibility(View.GONE); mScreenshotView.setVisibility(View.GONE); mScreenshotView.setLayerType(View.LAYER_TYPE_NONE, null); } }); if (!statusBarVisible || !navBarVisible) { // There is no status bar/nav bar, so just fade the screenshot away in place anim.setDuration(SCREENSHOT_FAST_DROP_OUT_DURATION); anim.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float t = (Float) animation.getAnimatedValue(); float scaleT = (SCREENSHOT_DROP_IN_MIN_SCALE + mBgPaddingScale) - t * (SCREENSHOT_DROP_IN_MIN_SCALE - SCREENSHOT_FAST_DROP_OUT_MIN_SCALE); mBackgroundView.setAlpha((1f - t) * BACKGROUND_ALPHA); mScreenshotView.setAlpha(1f - t); mScreenshotView.setScaleX(scaleT); mScreenshotView.setScaleY(scaleT); } }); } else { // In the case where there is a status bar, animate to the origin of the bar (top-left) final float scaleDurationPct = (float) SCREENSHOT_DROP_OUT_SCALE_DURATION / SCREENSHOT_DROP_OUT_DURATION; final Interpolator scaleInterpolator = new Interpolator() { @Override public float getInterpolation(float x) { if (x < scaleDurationPct) { // Decelerate, and scale the input accordingly return (float) (1f - Math.pow(1f - (x / scaleDurationPct), 2f)); } return 1f; } }; // Determine the bounds of how to scale float halfScreenWidth = (w - 2f * mBgPadding) / 2f; float halfScreenHeight = (h - 2f * mBgPadding) / 2f; final float offsetPct = SCREENSHOT_DROP_OUT_MIN_SCALE_OFFSET; final PointF finalPos = new PointF( -halfScreenWidth + (SCREENSHOT_DROP_OUT_MIN_SCALE + offsetPct) * halfScreenWidth, -halfScreenHeight + (SCREENSHOT_DROP_OUT_MIN_SCALE + offsetPct) * halfScreenHeight); // Animate the screenshot to the status bar anim.setDuration(SCREENSHOT_DROP_OUT_DURATION); anim.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float t = (Float) animation.getAnimatedValue(); float scaleT = (SCREENSHOT_DROP_IN_MIN_SCALE + mBgPaddingScale) - scaleInterpolator.getInterpolation(t) * (SCREENSHOT_DROP_IN_MIN_SCALE - SCREENSHOT_DROP_OUT_MIN_SCALE); mBackgroundView.setAlpha((1f - t) * BACKGROUND_ALPHA); mScreenshotView.setAlpha(1f - scaleInterpolator.getInterpolation(t)); mScreenshotView.setScaleX(scaleT); mScreenshotView.setScaleY(scaleT); mScreenshotView.setTranslationX(t * finalPos.x); mScreenshotView.setTranslationY(t * finalPos.y); } }); } return anim; } static void notifyScreenshotError(Context context, NotificationManager nManager) { Resources r = context.getResources(); // Clear all existing notification, compose the new notification and show it Notification.Builder b = new Notification.Builder(context) .setTicker(r.getString(R.string.screenshot_failed_title)) .setContentTitle(r.getString(R.string.screenshot_failed_title)) .setContentText(r.getString(R.string.screenshot_failed_text)) /*PRIZE-screenshot notification icon- liufan-2016-07-14-start*/ .setSmallIcon(R.drawable.stat_notify_image_error_prize) /*PRIZE-screenshot notification icon- liufan-2016-07-14-end*/ .setWhen(System.currentTimeMillis()) .setVisibility(Notification.VISIBILITY_PUBLIC) // ok to show outside lockscreen .setCategory(Notification.CATEGORY_ERROR) .setAutoCancel(true) .setColor(context.getColor( com.android.internal.R.color.system_notification_accent_color)); Notification n = new Notification.BigTextStyle(b) .bigText(r.getString(R.string.screenshot_failed_text)) .build(); /*PRIZE-don't change screenshot notification icon- liufan-2016-07-14-start*/ n.flags |= Notification.FLAG_KEEP_NOTIFICATION_ICON; /*PRIZE-don't change screenshot notification icon- liufan-2016-07-14-end*/ nManager.notify(R.id.notification_screenshot, n); } /** * Removes the notification for a screenshot after a share target is chosen. */ public static class TargetChosenReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (!intent.hasExtra(CANCEL_ID)) { return; } // Clear the notification final NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); final int id = intent.getIntExtra(CANCEL_ID, 0); nm.cancel(id); } } /** * Removes the last screenshot. */ public static class DeleteScreenshotReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (!intent.hasExtra(CANCEL_ID) || !intent.hasExtra(SCREENSHOT_URI_ID)) { return; } // Clear the notification final NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); final int id = intent.getIntExtra(CANCEL_ID, 0); final Uri uri = Uri.parse(intent.getStringExtra(SCREENSHOT_URI_ID)); nm.cancel(id); // And delete the image from the media store new DeleteImageInBackgroundTask(context).execute(uri); } } }
2) 静默安装接口
package com.prize.txInterface.thread; import java.io.File; import com.prize.txInterface.util.TXLog; import android.content.pm.IPackageInstallObserver; import android.content.pm.IPackageDeleteObserver; import android.R.string; import android.content.Context; import android.content.pm.PackageManager; import android.net.Uri; import android.os.Handler; import android.os.Message; public class InstallThread extends Thread { private int mInstallResult = 30; public static final int INSTALL_SUCCESS = 31; public static final int INSTALL_FAIL = 32; private int mUnInstallResult = 40; public static final int UNINSTALL_SUCCESS = 41; public static final int UNINSTALL_FAIL = 42; private String TAG = "snail_InstallThread"; private Context mContext; private Handler mHandler; private String mPakckageName,mFilePath; private boolean mIsInstall; public InstallThread(Context context, Handler handler,Boolean isInstall,String filePath,String pakckageName) { mContext = context; mHandler = handler; mPakckageName = pakckageName; mFilePath = filePath; mIsInstall = isInstall; TXLog.d(TAG, "---InstallThread---filePath=="+filePath+" pakcageName=="+pakckageName+" mIsInstall=="+mIsInstall); } @Override public void run() { try { if(mIsInstall){ installApkDefaul(mContext,mFilePath,mPakckageName); }else { uninstallApkDefaul(mContext,mPakckageName); } } catch (Exception e) { e.printStackTrace(); Message msg = mHandler.obtainMessage(); if(mIsInstall){ msg.what = INSTALL_FAIL; }else { msg.what = UNINSTALL_FAIL; } msg.obj = mPakckageName; mHandler.sendMessage(msg); } } // SilentInstall private void installApkDefaul(Context context, String filePath,String pakckageName) { File file = new File(filePath); int installFlags = 0; TXLog.d(TAG, "---SilentInstall----installApkDefaul----filePath=="+filePath+" pakcageName=="+pakckageName); if (!file.exists()) return; installFlags |= PackageManager.INSTALL_REPLACE_EXISTING; //installFlags |= PackageManager.INSTALL_FROM_PRIZE; PackageManager pm = context.getPackageManager(); IPackageInstallObserver observer = new MyPakcageInstallObserver(); TXLog.d(TAG, "---SilentInstall----installApkDefaul---installPackage----"); pm.installPackage(Uri.fromFile(file), observer, installFlags,pakckageName); } // SilentInstall callBack private class MyPakcageInstallObserver extends IPackageInstallObserver.Stub { @Override public void packageInstalled(String packageName, int returnCode) { // TODO Auto-generated method stub // 返回1代表安装成功 try { if(checkInstallResult(returnCode)) { mInstallResult = INSTALL_SUCCESS; } else { mInstallResult = INSTALL_FAIL; } TXLog.d(TAG, "---SilentInstall----packageInstalled----returnCode=="+returnCode+" pakcageName=="+packageName); if (mHandler != null) { Message msg = mHandler.obtainMessage(); msg.what = mInstallResult; msg.obj = packageName; mHandler.sendMessage(msg); } } catch (Exception e) { e.printStackTrace(); } } } // SilentUnInstall private void uninstallApkDefaul(Context context, String packageName) { TXLog.d(TAG, "---uninstallApkDefaul----pakcageName=="+packageName); PackageManager pm = context.getPackageManager(); IPackageDeleteObserver observer = new MyPackageDeleteObserver(); pm.deletePackage(packageName, observer, 0); } // SilentUnInstall callBack private class MyPackageDeleteObserver extends IPackageDeleteObserver.Stub { @Override public void packageDeleted(String packageName, int returnCode) { // TODO Auto-generated method stub // 返回1代表卸载成功 TXLog.d(TAG, "---SilentInstall----packageDeleted----returnCode=="+returnCode+" pakcageName=="+packageName); try { if(returnCode == 1) { mUnInstallResult = UNINSTALL_SUCCESS; } else { mUnInstallResult = UNINSTALL_FAIL; } if (mHandler != null) { Message msg = mHandler.obtainMessage(); msg.what = mUnInstallResult; msg.obj = packageName; mHandler.sendMessage(msg); } } catch (Exception e) { e.printStackTrace(); } } } }
3) 按键控制接口
-------frameworks/base/core/java/android/provider/Settings.java
+ /** @hide */ + public static final String PRIZE_SHOWING_NAVBAR_BACKBTN = "prize_navbar_backbtn"; + /** @hide */ + public static final String PRIZE_SHOWING_NAVBAR_HOMEBTN = "prize_navbar_homebtn"; + /** @hide */ + public static final String PRIZE_SHOWING_NAVBAR_RECENTBTN = "prize_navbar_recentbtn";
---------frameworks/base/packages/SettingsProvider/res/values/defaults.xml
<bool name="default_showing_backbtn">true</bool> <bool name="default_showing_homebtn">true</bool> <bool name="default_showing_recentbtn">true</bool>
loadBooleanSetting(stmt, Settings.System.PRIZE_SHOWING_NAVBAR_BACKBTN, R.bool.default_showing_backbtn); loadBooleanSetting(stmt, Settings.System.PRIZE_SHOWING_NAVBAR_HOMEBTN, R.bool.default_showing_homebtn); loadBooleanSetting(stmt, Settings.System.PRIZE_SHOWING_NAVBAR_RECENTBTN, R.bool.default_showing_recentbtn);
--------- frameworks/base/packages/PrizeSystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
private boolean mShowNavBackbtn = true; private boolean mShowNavHomebtn = true; private boolean mShowNavRecentbtn = true; mContext.getContentResolver().registerContentObserver( Settings.System.getUriFor(Settings.System.PRIZE_SHOWING_NAVBAR_BACKBTN), true, mShowNavBackbtnObserver); mContext.getContentResolver().registerContentObserver( Settings.System.getUriFor(Settings.System.PRIZE_SHOWING_NAVBAR_HOMEBTN), true, mShowNavHomebtnObserver); mContext.getContentResolver().registerContentObserver( Settings.System.getUriFor(Settings.System.PRIZE_SHOWING_NAVBAR_RECENTBTN), true, mShowNavRecentbtnObserver); private ContentObserver mShowNavBackbtnObserver = new ContentObserver(new Handler()) { @Override public void onChange(boolean selfChange) { printMyLog("mShowNavBackbtnObserver--onChange() --"); int show = Settings.System.getInt( mContext.getContentResolver(), Settings.System.PRIZE_SHOWING_NAVBAR_BACKBTN, 1); boolean isShow = (show == 1) ? true : false; if(isShow != mShowNavBackbtn && mNavigationBarView != null && mNavigationBarView.getBackButton() != null) { mShowNavBackbtn = isShow; if(mShowNavBackbtn){ mNavigationBarView.getBackButton().setVisibility(View.VISIBLE); }else{ mNavigationBarView.getBackButton().setVisibility(View.GONE); } } } }; private ContentObserver mShowNavHomebtnObserver = new ContentObserver(new Handler()) { @Override public void onChange(boolean selfChange) { printMyLog("mShowNavHomebtnObserver--onChange() --"); int show = Settings.System.getInt( mContext.getContentResolver(), Settings.System.PRIZE_SHOWING_NAVBAR_HOMEBTN, 1); boolean isShow = (show == 1) ? true : false; if(isShow != mShowNavHomebtn && mNavigationBarView != null &&mNavigationBarView.getHomeButton() != null) { mShowNavHomebtn = isShow; if(mShowNavHomebtn){ mNavigationBarView.getHomeButton().setVisibility(View.VISIBLE); }else{ mNavigationBarView.getHomeButton().setVisibility(View.GONE); } } } }; private ContentObserver mShowNavRecentbtnObserver = new ContentObserver(new Handler()) { @Override public void onChange(boolean selfChange) { printMyLog("mShowNavRecentbtnObserver--onChange() --"); int show = Settings.System.getInt( mContext.getContentResolver(), Settings.System.PRIZE_SHOWING_NAVBAR_RECENTBTN, 1); boolean isShow = (show == 1) ? true : false; if(isShow != mShowNavRecentbtn && mNavigationBarView != null &&mNavigationBarView.getRecentsButton() != null) { mShowNavRecentbtn = isShow; if(mShowNavRecentbtn){ mNavigationBarView.getRecentsButton().setVisibility(View.VISIBLE); }else{ mNavigationBarView.getRecentsButton().setVisibility(View.GONE); } } } }; mContext.getContentResolver().unregisterContentObserver(mShowNavBackbtnObserver); mContext.getContentResolver().unregisterContentObserver(mShowNavHomebtnObserver); mContext.getContentResolver().unregisterContentObserver(mShowNavRecentbtnObserver);
if(isshow){ getStatusBarManager(mContext).disable(StatusBarManager.DISABLE_NONE); }else { getStatusBarManager(mContext).disable(StatusBarManager.DISABLE_EXPAND); }
4) 关机/重启控制接口
//广播形式实现慢 /*Intent shutdown = new Intent(Intent.ACTION_REQUEST_SHUTDOWN); shutdown.putExtra(Intent.EXTRA_KEY_CONFIRM, false); //其中false换成true,会弹出是否关机的确认窗口 shutdown.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); RemoteService.this.getApplicationContext().startActivity(shutdown);*/ public static void shutdown(Context context){ PowerManager powermgr = (PowerManager) context.getApplicationContext().getSystemService(Context.POWER_SERVICE); try { Log.d("snail_","---shutdown------------------------"); powermgr.shutdown(false,"txinterface",false); } catch (Exception e) { TXLog.i(TAG, "shutdown e " + e.toString()); } } //广播形式实现慢 /*Intent reboot = new Intent(Intent.ACTION_REBOOT); reboot.putExtra("nowait", 1); reboot.putExtra("interval", 1); reboot.putExtra("window", 0); RemoteService.this.getApplicationContext().sendBroadcast(reboot); */ public static void reboot(Context context){ PowerManager powermgr = (PowerManager) context.getApplicationContext().getSystemService(Context.POWER_SERVICE); try { Log.d("snail_","---reboot------------------------"); powermgr.reboot("txinterface"); } catch (Exception e) { TXLog.i(TAG, "reboot e " + e.toString()); } }
5) 关闭应用接口
@Override //关闭应用 public void killApp(String pkg_name) throws RemoteException { // TODO Auto-generated method stub if(!TextUtils.isEmpty(pkg_name)){ getActivityManager().forceStopPackage(pkg_name); Utils.removeRecentTask(RemoteService.this.getApplicationContext(), pkg_name); } }
//移除最近任务栏记录 public static void removeRecentTask(Context context,String pkg_name){ ActivityManager activityManager = (ActivityManager) context.getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE); List<RecentTaskInfo> appTask = activityManager.getRecentTasks(Integer.MAX_VALUE, 1); if (appTask != null){ for (int i = 0; i < appTask.size(); i++) { TXLog.d(TAG, "----------removeRecentTask-----pkg=="+appTask.get(i).baseIntent.toString()+" pkg_name=="+pkg_name); if (appTask.get(i).baseIntent.toString().contains(pkg_name)){ TXLog.d(TAG, "----------removeRecentTask-----stackId=="+appTask.get(i).persistentId); if(appTask.get(i).persistentId != 0){ activityManager.removeTask(appTask.get(i).persistentId); break; } } } } }
6)控制应用使用
@Override public void setAppsEnable(String apppkg, boolean appenable)throws RemoteException { // TODO Auto-generated method stub DisableChangeUtils disallowdischanger; if(appenable){ disallowdischanger = new DisableChangeUtils(RemoteService.this.getApplicationContext().getPackageManager(),Arrays.asList(apppkg.split(",")), PackageManager.COMPONENT_ENABLED_STATE_DEFAULT); }else{ disallowdischanger = new DisableChangeUtils(RemoteService.this.getApplicationContext().getPackageManager(),Arrays.asList(apppkg.split(",")), PackageManager.COMPONENT_ENABLED_STATE_DISABLED); } disallowdischanger.execute((Object) null); } package com.prize.txInterface.util; import java.util.List; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.os.AsyncTask; public class DisableChangeUtils extends AsyncTask<Object, Object, Object>{ private static String TAG = "snail_DisableChanger"; private PackageManager mPm; private int mState; private List<String> mPackages; public DisableChangeUtils(PackageManager Pm, List<String> packages, int state) { mPm = Pm; mPackages = packages; mState = state; } @Override protected Object doInBackground(Object... params) { // TODO Auto-generated method stub List<PackageInfo> packageInfos = mPm.getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES); for (PackageInfo info : packageInfos) { ApplicationInfo appInfo = info.applicationInfo; for (int i = 0; i < mPackages.size(); i++) { if(appInfo.packageName.equals(mPackages.get(i))){ if (Utils.isThirdApp(appInfo)) { //use DONT_KILL_APP hideapp slow 10s see PMS.java sendPackageChangedBroadcast 19489 mPm.setApplicationEnabledSetting(appInfo.packageName, mState, 0); TXLog.d(TAG, "-----DisableChanger--doInBackground---isThirdApp-- name=="+appInfo.packageName+" mState=="+mState); }else { TXLog.d(TAG, "-----DisableChanger--doInBackground---!!!!!isThirdApp-- name=="+appInfo.packageName+" mState=="+mState); mPm.setApplicationEnabledSetting(appInfo.packageName, mState, 0);//PackageManager.DONT_KILL_APP } } } } return null; } } public static boolean isThirdApp(ApplicationInfo pInfo) { if ((pInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) { return true; } else if ((pInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) { return true; } return false; }
8) 控制推送通知接口
public static void setNotificationEnable(Context mContext,boolean isenable){ List<PackageInfo> packageInfos = getPackageManager(mContext).getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES); for (PackageInfo info : packageInfos) { ApplicationInfo appInfo = info.applicationInfo; if (Utils.isThirdApp(appInfo)) { try { getNotificationManager(mContext).setNotificationsEnabledForPackage(appInfo.packageName, appInfo.uid, isenable); } catch (Exception e) { } } } }
9) 控制SD/TF卡接口
@Override public void setSDCardEnable(boolean sdenble) throws RemoteException { // TODO Auto-generated method stub if(sdenble){ MountTaskUtil mount_task = new MountTaskUtil(RemoteService.this.getApplicationContext()); mount_task.execute(); }else { UnmountTaskUtils unmount_task = new UnmountTaskUtils(RemoteService.this.getApplicationContext()); unmount_task.execute(); } } package com.prize.txInterface.util; import java.io.File; import java.util.List; import com.prize.txInterface.R; import android.content.Context; import android.os.AsyncTask; import android.os.Environment; import android.os.storage.StorageManager; import android.os.storage.VolumeInfo; import android.os.SystemProperties; import android.text.TextUtils; import android.util.Log; import android.widget.Toast; public class MountTaskUtil extends AsyncTask<Void, Void, Exception> { private String TAG = "snail_MountTaskUtil"; private Context mContext; private StorageManager mStorageManager; public MountTaskUtil(Context context) { mContext = context.getApplicationContext(); mStorageManager = mContext.getSystemService(StorageManager.class); } @Override protected Exception doInBackground(Void... params) { // TODO Auto-generated method stub try { final List<VolumeInfo> volumes = mStorageManager.getVolumes(); for (VolumeInfo vol : volumes) { Log.d("snail", "--MountTaskUtil--------VolumeInfo--vol=="+vol.getId()+" getType()=="+vol.getType()+" getState()=="+vol.getState()); if (vol.getType() == VolumeInfo.TYPE_PUBLIC && vol.getState()==VolumeInfo.STATE_UNMOUNTED &&!TextUtils.isEmpty(vol.getId())) { mStorageManager.mount(vol.getId()); } } return null; } catch (Exception e) { return e; } } @Override protected void onPostExecute(Exception result) { // TODO Auto-generated method stub super.onPostExecute(result); if (result == null) { TXLog.d(TAG, mContext.getString(R.string.storage_mount_success)); } else { TXLog.d(TAG, mContext.getString(R.string.storage_mount_success)+" error=="+result.getMessage()); } } } package com.prize.txInterface.util; import java.util.List; import com.prize.txInterface.R; import android.content.Context; import android.os.AsyncTask; import android.os.Environment; import android.os.storage.StorageManager; import android.os.storage.VolumeInfo; import android.os.SystemProperties; import android.text.TextUtils; import android.util.Log; import android.widget.Toast; public class UnmountTaskUtils extends AsyncTask<Void, Void, Exception> { private String TAG = "snail_MountTaskUtil"; private Context mContext; private StorageManager mStorageManager; private final Object mLock = new Object(); public UnmountTaskUtils(Context context) { mContext = context.getApplicationContext(); mStorageManager = mContext.getSystemService(StorageManager.class); } @Override protected Exception doInBackground(Void... params) { // TODO Auto-generated method stub try { final List<VolumeInfo> volumes = mStorageManager.getVolumes(); for (VolumeInfo vol : volumes) { Log.d("snail", "-------UnmountTaskUtils---VolumeInfo--vol=="+vol.getId()+" getType()=="+vol.getType()+" getState()=="+vol.getState()); if (vol.getType() == VolumeInfo.TYPE_PUBLIC && vol.getState()==VolumeInfo.STATE_MOUNTED &&!TextUtils.isEmpty(vol.getId())) { mStorageManager.unmount(vol.getId()); } } return null; } catch (Exception e) { return e; } } @Override protected void onPostExecute(Exception result) { // TODO Auto-generated method stub super.onPostExecute(result); if (result == null) { TXLog.d(TAG, mContext.getString(R.string.storage_unmount_success)); } else { TXLog.d(TAG, mContext.getString(R.string.storage_unmount_failure)+" error=="+result.getMessage()); } } }
10) 控制USB口接口
------ frameworks/base/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -1394,7 +1394,7 @@ public class UsbDeviceManager { mUsbConfigured = mConfigured; if (!mConnected) { // When a disconnect occurs, relock access to sensitive user data mUsbDataUnlocked = false; ------ // mUsbDataUnlocked = false; } updateUsbNotification(); updateAdbNotification(); ... ... @@ -1403,7 +1403,7 @@ public class UsbDeviceManager { updateCurrentAccessory(); } else if (!mConnected) { // restore defaults when USB is disconnected setEnabledFunctions(null, false); -------- //setEnabledFunctions(null, false); } if (mBootCompleted) { updateUsbStateBroadcastIfNeeded();
@Override public void setUSBtransferEnable(boolean enable) throws RemoteException { // TODO Auto-generated method stub String usbMode = Utils.getUsbDataMode(RemoteService.this.getApplicationContext()); if(!TextUtils.isEmpty(usbMode)){ if(enable && !usbMode.equals("mtp")){ Utils.setUsbDataMode(RemoteService.this.getApplicationContext(), UsbManager.USB_FUNCTION_MTP); }else if(!enable && !usbMode.equals("charge")){ Utils.setUsbDataMode(RemoteService.this.getApplicationContext(), null); } } } public static void setUsbDataMode(Context context,String mode) { UsbManager mUsbManager = (UsbManager)context.getApplicationContext().getSystemService(Context.USB_SERVICE); TXLog.d(TAG, "--------setUsbDataMode-----------mode==="+mode); if(TextUtils.isEmpty(mode)){ mUsbManager.setCurrentFunction(null); mUsbManager.setUsbDataUnlocked(false); }else { mUsbManager.setCurrentFunction(mode); mUsbManager.setUsbDataUnlocked(true); } }