android 关于屏幕截屏的几种办法_screen_short_side-程序员宅基地

技术标签: android开发  android应用  android  


年末较闲,就上个星期查找各方面的资料关于android截图事情,自已也测试一些代码,已改改进或者优化。接下来进行总结一下。其实,如果真正android系统截屏是需要root权限的。但要用户使用都root,似乎不可能,体验性太差了。这期间我稍微观察了QQ的截图。QQ的截图是摇一摇截图当前屏幕,这个是不需要root权限的。然而,如果你要截图页面不是当前页面,就需要root权限。可以使用360超级root尝试一下。

本文demo下载地址:http://download.csdn.net/detail/qq_16064871/9420810

1、直接使用getWindow().getDecorView().getRootView()

直接使用getWindow().getDecorView().getRootView()是获取当前屏幕的activity。然而对于系统状态栏的信息是截不了,出现一条空白的。如下图:



主要到没,有一条白色边就是系统状态栏。看一下代码,很简单都加了注释了。

[java]  view plain   copy
  1. //这种方法状态栏是空白,显示不了状态栏的信息  
  2. private void saveCurrentImage()    
  3.    {    
  4.        //获取当前屏幕的大小  
  5.     int width = getWindow().getDecorView().getRootView().getWidth();  
  6.        int height = getWindow().getDecorView().getRootView().getHeight();  
  7.        //生成相同大小的图片  
  8.        Bitmap temBitmap = Bitmap.createBitmap( width, height, Config.ARGB_8888 );      
  9.        //找到当前页面的跟布局  
  10.        View view =  getWindow().getDecorView().getRootView();  
  11.        //设置缓存  
  12.        view.setDrawingCacheEnabled(true);  
  13.        view.buildDrawingCache();  
  14.        //从缓存中获取当前屏幕的图片  
  15.        temBitmap = view.getDrawingCache();  
  16.   
  17.     //输出到sd卡  
  18.     if (FileIOUtil.getExistStorage()) {  
  19.         FileIOUtil.GetInstance().onFolderAnalysis(FileIOUtil.GetInstance().getFilePathAndName());  
  20.         File file = new File(FileIOUtil.GetInstance().getFilePathAndName());    
  21.         try {  
  22.             if (!file.exists()) {    
  23.                 file.createNewFile();    
  24.             }   
  25.             FileOutputStream foStream = new FileOutputStream(file);  
  26.             temBitmap.compress(Bitmap.CompressFormat.PNG, 100, foStream);  
  27.             foStream.flush();    
  28.             foStream.close();  
  29.         } catch (Exception e) {  
  30.             Log.i("Show", e.toString());  
  31.         }  
  32.     }  
  33.    }  

2、自定义view控件的截图

自定义view控件都是继承view的吗,那么就有可以获取宽度,高度。生成图片,把它绘制出来的。我拿了直接写的自定义随机验证码的例子来截图。


[java]  view plain   copy
  1.    //保存自定义view的截图  
  2. private void saveCustomViewBitmap() {  
  3.     //获取自定义view图片的大小  
  4.     Bitmap temBitmap = Bitmap.createBitmap(mCodeView.getWidth(), mCodeView.getHeight(), Config.ARGB_8888);  
  5.     //使用Canvas,调用自定义view控件的onDraw方法,绘制图片  
  6.     Canvas canvas = new Canvas(temBitmap);    
  7.     mCodeView.onDraw(canvas);  
  8.       
  9.     //输出到sd卡  
  10.     if (FileIOUtil.getExistStorage()) {  
  11.         FileIOUtil.GetInstance().onFolderAnalysis(FileIOUtil.GetInstance().getFilePathAndName());  
  12.         File file = new File(FileIOUtil.GetInstance().getFilePathAndName());    
  13.         try {  
  14.             if (!file.exists()) {    
  15.                 file.createNewFile();    
  16.             }   
  17.             FileOutputStream foStream = new FileOutputStream(file);  
  18.             temBitmap.compress(Bitmap.CompressFormat.PNG, 100, foStream);  
  19.             foStream.flush();    
  20.             foStream.close();  
  21.             Toast.makeText(MainActivity.this"截屏文件已保存至" + FileIOUtil.GetInstance().getFilePathAndName(), Toast.LENGTH_LONG).show();   
  22.         } catch (Exception e) {  
  23.             Log.i("Show", e.toString());  
  24.         }  
  25.     }  
  26. }  
有人根据这种思路,自定义整个布局的view,然后来截图。也是够拼的,我有点不赞成这样使用。

3、基于android ddmlib截屏

这个是java写的一个类,入口是mian函数。那么这种实现方式是要android连接着电脑,还需要android设备调试。这种不多说,搞android开发都懂。太麻烦了,我们也不使用。

[java]  view plain   copy
  1. package com.example.screenshot;  
  2.   
  3. import java.io.IOException;  
  4. import java.text.SimpleDateFormat;  
  5. import java.util.Date;  
  6.   
  7. import com.android.ddmlib.AdbCommandRejectedException;  
  8. import com.android.ddmlib.AndroidDebugBridge;  
  9. import com.android.ddmlib.IDevice;  
  10. import com.android.ddmlib.RawImage;  
  11. import com.android.ddmlib.TimeoutException;  
  12.   
  13. public class ScreenShoddmlib {  
  14.     private BufferedImage image = null;    
  15.      /**  
  16.       * @param args  
  17.       */    
  18.      public static void main(String[] args) {    
  19.       // TODO Auto-generated method stub    
  20.       AndroidDebugBridge.init(false); //    
  21.       ScreenShoddmlib screenshot = new ScreenShoddmlib();    
  22.       IDevice device = screenshot.getDevice();    
  23.           
  24.       for (int i = 0; i < 10; i++) {    
  25.        Date date=new Date();    
  26.        SimpleDateFormat df=new SimpleDateFormat("MM-dd-HH-mm-ss");     
  27.        String nowTime = df.format(date);    
  28.        screenshot.getScreenShot(device, "Robotium" + nowTime);    
  29.        try {    
  30.         Thread.sleep(1000);    
  31.        } catch (InterruptedException e) {    
  32.         // TODO Auto-generated catch block    
  33.         e.printStackTrace();    
  34.        }    
  35.       }    
  36.      }    
  37.         
  38.          
  39.      public void getScreenShot(IDevice device,String filename) {    
  40.       RawImage rawScreen = null;    
  41.       try {    
  42.        rawScreen = device.getScreenshot();    
  43.       } catch (TimeoutException e) {    
  44.        // TODO Auto-generated catch block    
  45.        e.printStackTrace();    
  46.       } catch (AdbCommandRejectedException e) {    
  47.        // TODO Auto-generated catch block    
  48.        e.printStackTrace();    
  49.       } catch (IOException e) {    
  50.        // TODO Auto-generated catch block    
  51.        e.printStackTrace();    
  52.       }    
  53.       if (rawScreen != null) {    
  54.        Boolean landscape = false;    
  55.        int width2 = landscape ? rawScreen.height : rawScreen.width;    
  56.        int height2 = landscape ? rawScreen.width : rawScreen.height;    
  57.        if (image == null) {    
  58.         image = new BufferedImage(width2, height2,    
  59.           BufferedImage.TYPE_INT_RGB);    
  60.        } else {    
  61.         if (image.getHeight() != height2 || image.getWidth() != width2) {    
  62.          image = new BufferedImage(width2, height2,    
  63.            BufferedImage.TYPE_INT_RGB);    
  64.         }    
  65.        }    
  66.        int index = 0;    
  67.        int indexInc = rawScreen.bpp >> 3;    
  68.        for (int y = 0; y < rawScreen.height; y++) {    
  69.         for (int x = 0; x < rawScreen.width; x++, index += indexInc) {    
  70.          int value = rawScreen.getARGB(index);    
  71.          if (landscape)    
  72.           image.setRGB(y, rawScreen.width - x - 1, value);    
  73.          else    
  74.           image.setRGB(x, y, value);    
  75.         }    
  76.        }    
  77.        try {    
  78.         ImageIO.write((RenderedImage) image, "PNG"new File("D:/"    
  79.           + filename + ".jpg"));    
  80.        } catch (IOException e) {    
  81.         // TODO Auto-generated catch block    
  82.         e.printStackTrace();    
  83.        }    
  84.       }    
  85.      }    
  86.         
  87.      /**  
  88.       * 获取得到device对象  
  89.       * @return  
  90.       */    
  91.      private IDevice getDevice(){    
  92.       IDevice device;    
  93.       AndroidDebugBridge bridge = AndroidDebugBridge    
  94.         .createBridge("adb"true);//如果代码有问题请查看API,修改此处的参数值试一下    
  95.       waitDevicesList(bridge);    
  96.       IDevice devices[] = bridge.getDevices();    
  97.       device = devices[0];    
  98.       return device;    
  99.      }    
  100.          
  101.      /**  
  102.       * 等待查找device  
  103.       * @param bridge  
  104.       */    
  105.      private void waitDevicesList(AndroidDebugBridge bridge) {    
  106.       int count = 0;    
  107.       while (bridge.hasInitialDeviceList() == false) {    
  108.        try {    
  109.         Thread.sleep(500);     
  110.         count++;    
  111.        } catch (InterruptedException e) {    
  112.        }    
  113.        if (count > 240) {    
  114.         System.err.print("等待获取设备超时");    
  115.         break;    
  116.        }    
  117.       }    
  118. }  

4、使用adb命令

需要系统权限,在APK中调用“adb shell screencap -pfilepath” 命令

需要获得系统权限:1、 在AndroidManifest.xml文件中添加 <uses-permissionandroid:name="android.permission.READ_FRAME_BUFFER"/>
                     2、修改APK为系统权限,将APK放到源码中编译, 修改Android.mk     LOCAL_CERTIFICATE := platform
在这里我要说一下,搞过jni调用就知道Android.mk的作用。此举也是麻烦,效果也不是很好。

[java]  view plain   copy
  1. public void takeScreenShot(){   
  2.     String mSavedPath = Environment.getExternalStorageDirectory()+File. separator + "screenshot.png" ;   
  3. try {                       
  4.            Runtime. getRuntime().exec("screencap -p " + mSavedPath);   
  5.     } catch (Exception e) {   
  6.            e.printStackTrace();   
  7.     }   

5、看一下系统截屏是怎样的

相信大家都知道,三星的机子是同时按下 home键 + 电源键 3秒截图。

如果没有home键的机子是按下 音量键向下那个 + 电源键 3秒截图的。

获取物理键盘按下的源码:PhoneWindowManager.java

[java]  view plain   copy
  1. // Handle special keys.  
  2.  switch (keyCode) {  
  3.      case KeyEvent.KEYCODE_VOLUME_DOWN:  
  4.      case KeyEvent.KEYCODE_VOLUME_UP:  
  5.      case KeyEvent.KEYCODE_VOLUME_MUTE: {  
  6.          if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {  
  7.              if (down) {  
  8.                  if (isScreenOn && !mVolumeDownKeyTriggered  
  9.                          && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {  
  10.                      mVolumeDownKeyTriggered = true;  
  11.                      mVolumeDownKeyTime = event.getDownTime();  
  12.                      mVolumeDownKeyConsumedByScreenshotChord = false;  
  13.                      cancelPendingPowerKeyAction();  
  14.                      interceptScreenshotChord();  
  15.                  }  
  16.              } else {  
  17.                  mVolumeDownKeyTriggered = false;  
  18.                  cancelPendingScreenshotChordAction();  
  19.              }  
  20.          } else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {  
  21.              if (down) {  
  22.                  if (isScreenOn && !mVolumeUpKeyTriggered  
  23.                          && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {  
  24.                      mVolumeUpKeyTriggered = true;  
  25.                      cancelPendingPowerKeyAction();  
  26.                      cancelPendingScreenshotChordAction();  
  27.                  }  
  28.              } else {  
  29.                  mVolumeUpKeyTriggered = false;  
  30.                  cancelPendingScreenshotChordAction();  
  31.              }  
  32.          }  
  33.          if (down) {  
  34.              ITelephony telephonyService = getTelephonyService();  
  35.              if (telephonyService != null) {  
  36.                  try {  
  37.                      if (telephonyService.isRinging()) {  
  38.                          // If an incoming call is ringing, either VOLUME key means  
  39.                          // "silence ringer".  We handle these keys here, rather than  
  40.                          // in the InCallScreen, to make sure we'll respond to them  
  41.                          // even if the InCallScreen hasn't come to the foreground yet.  
  42.                          // Look for the DOWN event here, to agree with the "fallback"  
  43.                          // behavior in the InCallScreen.  
  44.                          Log.i(TAG, "interceptKeyBeforeQueueing:"  
  45.                                + " VOLUME key-down while ringing: Silence ringer!");  
  46.   
  47.                          // Silence the ringer.  (It's safe to call this  
  48.                          // even if the ringer has already been silenced.)  
  49.                          telephonyService.silenceRinger();  
  50.   
  51.                          // And *don't* pass this key thru to the current activity  
  52.                          // (which is probably the InCallScreen.)  
  53.                          result &= ~ACTION_PASS_TO_USER;  
  54.                          break;  
  55.                      }  
  56.                      if (telephonyService.isOffhook()  
  57.                              && (result & ACTION_PASS_TO_USER) == 0) {  
  58.                          // If we are in call but we decided not to pass the key to  
  59.                          // the application, handle the volume change here.  
  60.                          handleVolumeKey(AudioManager.STREAM_VOICE_CALL, keyCode);  
  61.                          break;  
  62.                      }  
  63.                  } catch (RemoteException ex) {  
  64.                      Log.w(TAG, "ITelephony threw RemoteException", ex);  
  65.                  }  
  66.              }  
  67.   
  68.              if (isMusicActive() && (result & ACTION_PASS_TO_USER) == 0) {  
  69.                  // If music is playing but we decided not to pass the key to the  
  70.                  // application, handle the volume change here.  
  71.                  handleVolumeKey(AudioManager.STREAM_MUSIC, keyCode);  
  72.                  break;  
  73.              }  
  74.          }  
  75.          break;  
  76.      }  
  77.   
  78.      case KeyEvent.KEYCODE_ENDCALL: {  
  79.          result &= ~ACTION_PASS_TO_USER;  
  80.          if (down) {  
  81.              ITelephony telephonyService = getTelephonyService();  
  82.              boolean hungUp = false;  
  83.              if (telephonyService != null) {  
  84.                  try {  
  85.                      hungUp = telephonyService.endCall();  
  86.                  } catch (RemoteException ex) {  
  87.                      Log.w(TAG, "ITelephony threw RemoteException", ex);  
  88.                  }  
  89.              }  
  90.              interceptPowerKeyDown(!isScreenOn || hungUp);  
  91.          } else {  
  92.              if (interceptPowerKeyUp(canceled)) {  
  93.                  if ((mEndcallBehavior  
  94.                          & Settings.System.END_BUTTON_BEHAVIOR_HOME) != 0) {  
  95.                      if (goHome()) {  
  96.                          break;  
  97.                      }  
  98.                  }  
  99.                  if ((mEndcallBehavior  
  100.                          & Settings.System.END_BUTTON_BEHAVIOR_SLEEP) != 0) {  
  101.                      result = (result & ~ACTION_WAKE_UP) | ACTION_GO_TO_SLEEP;  
  102.                  }  
  103.              }  
  104.          }  
  105.          break;  
  106.      }  
  107.   
  108.      case KeyEvent.KEYCODE_POWER: {  
  109.          result &= ~ACTION_PASS_TO_USER;  
  110.          if (down) {  
  111.              if (isScreenOn && !mPowerKeyTriggered  
  112.                      && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {  
  113.                  mPowerKeyTriggered = true;  
  114.                  mPowerKeyTime = event.getDownTime();  
  115.                  interceptScreenshotChord();  
  116.              }  
  117.   
  118.              ITelephony telephonyService = getTelephonyService();  
  119.              boolean hungUp = false;  
  120.              if (telephonyService != null) {  
  121.                  try {  
  122.                      if (telephonyService.isRinging()) {  
  123.                          // Pressing Power while there's a ringing incoming  
  124.                          // call should silence the ringer.  
  125.                          telephonyService.silenceRinger();  
  126.                      } else if ((mIncallPowerBehavior  
  127.                              & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0  
  128.                              && telephonyService.isOffhook()) {  
  129.                          // Otherwise, if "Power button ends call" is enabled,  
  130.                          // the Power button will hang up any current active call.  
  131.                          hungUp = telephonyService.endCall();  
  132.                      }  
  133.                  } catch (RemoteException ex) {  
  134.                      Log.w(TAG, "ITelephony threw RemoteException", ex);  
  135.                  }  
  136.              }  
  137.              interceptPowerKeyDown(!isScreenOn || hungUp  
  138.                      || mVolumeDownKeyTriggered || mVolumeUpKeyTriggered);  
  139.          } else {  
  140.              mPowerKeyTriggered = false;  
  141.              cancelPendingScreenshotChordAction();  
  142.              if (interceptPowerKeyUp(canceled || mPendingPowerKeyUpCanceled)) {  
  143.                  result = (result & ~ACTION_WAKE_UP) | ACTION_GO_TO_SLEEP;  
  144.              }  
  145.              mPendingPowerKeyUpCanceled = false;  
  146.          }  
  147.          break;  
  148.      }  


响应截屏的方法:

[java]  view plain   copy
  1. private void interceptScreenshotChord() {  
  2.     if (mScreenshotChordEnabled  
  3.             && mVolumeDownKeyTriggered && mPowerKeyTriggered && !mVolumeUpKeyTriggered) {  
  4.         final long now = SystemClock.uptimeMillis();  
  5.         if (now <= mVolumeDownKeyTime + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS  
  6.                 && now <= mPowerKeyTime + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS) {  
  7.             mVolumeDownKeyConsumedByScreenshotChord = true;  
  8.             cancelPendingPowerKeyAction();  
  9.   
  10.             mHandler.postDelayed(mScreenshotChordLongPress, getScreenshotChordLongPressDelay());  
  11.         }  
  12.     }  
  13. }  
  14.   
  15. private long getScreenshotChordLongPressDelay() {  
  16.     if (mKeyguardMediator.isShowing()) {  
  17.         // Double the time it takes to take a screenshot from the keyguard  
  18.         return (long) (KEYGUARD_SCREENSHOT_CHORD_DELAY_MULTIPLIER *  
  19.                 ViewConfiguration.getGlobalActionKeyTimeout());  
  20.     } else {  
  21.         return ViewConfiguration.getGlobalActionKeyTimeout();  
  22.     }  
  23. }  

接受响应的服务

[java]  view plain   copy
  在CODE上查看代码片 派生到我的代码片
  1. private final Runnable mScreenshotChordLongPress = new Runnable() {  
  2.     public void run() {  
  3.         takeScreenshot();  
  4.     }  
  5. };  

[java]  view plain   copy
  在CODE上查看代码片 派生到我的代码片
  1. private void takeScreenshot() {  
  2.     synchronized (mScreenshotLock) {  
  3.         if (mScreenshotConnection != null) {  
  4.             return;  
  5.         }  
  6.         ComponentName cn = new ComponentName("com.android.systemui",  
  7.                 "com.android.systemui.screenshot.TakeScreenshotService");  
  8.         Intent intent = new Intent();  
  9.         intent.setComponent(cn);  
  10.         ServiceConnection conn = new ServiceConnection() {  
  11.             @Override  
  12.             public void onServiceConnected(ComponentName name, IBinder service) {  
  13.                 synchronized (mScreenshotLock) {  
  14.                     if (mScreenshotConnection != this) {  
  15.                         return;  
  16.                     }  
  17.                     Messenger messenger = new Messenger(service);  
  18.                     Message msg = Message.obtain(null1);  
  19.                     final ServiceConnection myConn = this;  
  20.                     Handler h = new Handler(mHandler.getLooper()) {  
  21.                         @Override  
  22.                         public void handleMessage(Message msg) {  
  23.                             synchronized (mScreenshotLock) {  
  24.                                 if (mScreenshotConnection == myConn) {  
  25.                                     mContext.unbindService(mScreenshotConnection);  
  26.                                     mScreenshotConnection = null;  
  27.                                     mHandler.removeCallbacks(mScreenshotTimeout);  
  28.                                 }  
  29.                             }  
  30.                         }  
  31.                     };  
  32.                     msg.replyTo = new Messenger(h);  
  33.                     msg.arg1 = msg.arg2 = 0;  
  34.                     if (mStatusBar != null && mStatusBar.isVisibleLw())  
  35.                         msg.arg1 = 1;  
  36.                     if (mNavigationBar != null && mNavigationBar.isVisibleLw())  
  37.                         msg.arg2 = 1;  
  38.                     try {  
  39.                         messenger.send(msg);  
  40.                     } catch (RemoteException e) {  
  41.                     }  
  42.                 }  
  43.             }  
  44.             @Override  
  45.             public void onServiceDisconnected(ComponentName name) {}  
  46.         };  
  47.         if (mContext.bindService(  
  48.                 intent, conn, Context.BIND_AUTO_CREATE, UserHandle.USER_CURRENT)) {  
  49.             mScreenshotConnection = conn;  
  50.             mHandler.postDelayed(mScreenshotTimeout, 10000);  
  51.         }  
  52.     }  
  53. }  

启动时这个服务 ComponentName cn = new ComponentName("com.android.systemui", "com.android.systemui.screenshot.TakeScreenshotService");

[java]  view plain   copy
  在CODE上查看代码片 派生到我的代码片
  1. package com.android.systemui.screenshot;  
  2.   
  3. import android.app.Service;  
  4. import android.content.Intent;  
  5. import android.os.Handler;  
  6. import android.os.IBinder;  
  7. import android.os.Message;  
  8. import android.os.Messenger;  
  9. import android.os.RemoteException;  
  10.   
  11. public class TakeScreenshotService extends Service {  
  12.     private static final String TAG = "TakeScreenshotService";  
  13.   
  14.     private static GlobalScreenshot mScreenshot;  
  15.   
  16.     private Handler mHandler = new Handler() {  
  17.         @Override  
  18.         public void handleMessage(Message msg) {  
  19.             switch (msg.what) {  
  20.                 case 1:  
  21.                     final Messenger callback = msg.replyTo;  
  22.                     if (mScreenshot == null) {  
  23.                         mScreenshot = new GlobalScreenshot(TakeScreenshotService.this);  
  24.                     }  
  25.                     mScreenshot.takeScreenshot(new Runnable() {  
  26.                         @Override public void run() {  
  27.                             Message reply = Message.obtain(null1);  
  28.                             try {  
  29.                                 callback.send(reply);  
  30.                             } catch (RemoteException e) {  
  31.                             }  
  32.                         }  
  33.                     }, msg.arg1 > 0, msg.arg2 > 0);  
  34.             }  
  35.         }  
  36.     };  
  37.   
  38.     @Override  
  39.     public IBinder onBind(Intent intent) {  
  40.         return new Messenger(mHandler).getBinder();  
  41.     }  
再往下就是底层库,需要编译出来才能看得到了。这些就先不研究了。

6、还有部分系统源码是截图的

[java]  view plain   copy
  在CODE上查看代码片 派生到我的代码片
  1. /* 
  2.  * Copyright (C) 2011 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.   
  17. package com.android.systemui.screenshot;  
  18.   
  19. import android.animation.Animator;  
  20. import android.animation.AnimatorListenerAdapter;  
  21. import android.animation.AnimatorSet;  
  22. import android.animation.ValueAnimator;  
  23. import android.animation.ValueAnimator.AnimatorUpdateListener;  
  24. import android.app.Notification;  
  25. import android.app.Notification.BigPictureStyle;  
  26. import android.app.NotificationManager;  
  27. import android.app.PendingIntent;  
  28. import android.content.ContentResolver;  
  29. import android.content.ContentValues;  
  30. import android.content.Context;  
  31. import android.content.Intent;  
  32. import android.content.res.Resources;  
  33. import android.graphics.Bitmap;  
  34. import android.graphics.Canvas;  
  35. import android.graphics.ColorMatrix;  
  36. import android.graphics.ColorMatrixColorFilter;  
  37. import android.graphics.Matrix;  
  38. import android.graphics.Paint;  
  39. import android.graphics.PixelFormat;  
  40. import android.graphics.PointF;  
  41. import android.media.MediaActionSound;  
  42. import android.net.Uri;  
  43. import android.os.AsyncTask;  
  44. import android.os.Environment;  
  45. import android.os.Process;  
  46. import android.provider.MediaStore;  
  47. import android.util.DisplayMetrics;  
  48. import android.view.Display;  
  49. import android.view.LayoutInflater;  
  50. import android.view.MotionEvent;  
  51. import android.view.Surface;  
  52. import android.view.View;  
  53. import android.view.ViewGroup;  
  54. import android.view.WindowManager;  
  55. import android.view.animation.Interpolator;  
  56. import android.widget.ImageView;  
  57.   
  58. import com.android.systemui.R;  
  59.   
  60. import java.io.File;  
  61. import java.io.OutputStream;  
  62. import java.text.SimpleDateFormat;  
  63. import java.util.Date;  
  64.   
  65. /** 
  66.  * POD used in the AsyncTask which saves an image in the background. 
  67.  */  
  68. class SaveImageInBackgroundData {  
  69.     Context context;  
  70.     Bitmap image;  
  71.     Uri imageUri;  
  72.     Runnable finisher;  
  73.     int iconSize;  
  74.     int result;  
  75. }  
  76.   
  77. /** 
  78.  * An AsyncTask that saves an image to the media store in the background. 
  79.  */  
  80. class SaveImageInBackgroundTask extends AsyncTask<SaveImageInBackgroundData, Void,  
  81.         SaveImageInBackgroundData> {  
  82.     private static final String SCREENSHOTS_DIR_NAME = "Screenshots";  
  83.     private static final String SCREENSHOT_FILE_NAME_TEMPLATE = "Screenshot_%s.png";  
  84.     private static final String SCREENSHOT_FILE_PATH_TEMPLATE = "%s/%s/%s";  
  85.   
  86.     private int mNotificationId;  
  87.     private NotificationManager mNotificationManager;  
  88.     private Notification.Builder mNotificationBuilder;  
  89.     private String mImageFileName;  
  90.     private String mImageFilePath;  
  91.     private long mImageTime;  
  92.     private BigPictureStyle mNotificationStyle;  
  93.   
  94.     // WORKAROUND: We want the same notification across screenshots that we update so that we don't  
  95.     // spam a user's notification drawer.  However, we only show the ticker for the saving state  
  96.     // and if the ticker text is the same as the previous notification, then it will not show. So  
  97.     // for now, we just add and remove a space from the ticker text to trigger the animation when  
  98.     // necessary.  
  99.     private static boolean mTickerAddSpace;  
  100.   
  101.     SaveImageInBackgroundTask(Context context, SaveImageInBackgroundData data,  
  102.             NotificationManager nManager, int nId) {  
  103.         Resources r = context.getResources();  
  104.   
  105.         // Prepare all the output metadata  
  106.         mImageTime = System.currentTimeMillis();  
  107.         String imageDate = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date(mImageTime));  
  108.         String imageDir = Environment.getExternalStoragePublicDirectory(  
  109.                 Environment.DIRECTORY_PICTURES).getAbsolutePath();  
  110.         mImageFileName = String.format(SCREENSHOT_FILE_NAME_TEMPLATE, imageDate);  
  111.         mImageFilePath = String.format(SCREENSHOT_FILE_PATH_TEMPLATE, imageDir,  
  112.                 SCREENSHOTS_DIR_NAME, mImageFileName);  
  113.   
  114.         // Create the large notification icon  
  115.         int imageWidth = data.image.getWidth();  
  116.         int imageHeight = data.image.getHeight();  
  117.         int iconSize = data.iconSize;  
  118.   
  119.         final int shortSide = imageWidth < imageHeight ? imageWidth : imageHeight;  
  120.         Bitmap preview = Bitmap.createBitmap(shortSide, shortSide, data.image.getConfig());  
  121.         Canvas c = new Canvas(preview);  
  122.         Paint paint = new Paint();  
  123.         ColorMatrix desat = new ColorMatrix();  
  124.         desat.setSaturation(0.25f);  
  125.         paint.setColorFilter(new ColorMatrixColorFilter(desat));  
  126.         Matrix matrix = new Matrix();  
  127.         matrix.postTranslate((shortSide - imageWidth) / 2,  
  128.                             (shortSide - imageHeight) / 2);  
  129.         c.drawBitmap(data.image, matrix, paint);  
  130.         c.drawColor(0x40FFFFFF);  
  131.   
  132.         Bitmap croppedIcon = Bitmap.createScaledBitmap(preview, iconSize, iconSize, true);  
  133.   
  134.         // Show the intermediate notification  
  135.         mTickerAddSpace = !mTickerAddSpace;  
  136.         mNotificationId = nId;  
  137.         mNotificationManager = nManager;  
  138.         mNotificationBuilder = new Notification.Builder(context)  
  139.             .setTicker(r.getString(R.string.screenshot_saving_ticker)  
  140.                     + (mTickerAddSpace ? " " : ""))  
  141.             .setContentTitle(r.getString(R.string.screenshot_saving_title))  
  142.             .setContentText(r.getString(R.string.screenshot_saving_text))  
  143.             .setSmallIcon(R.drawable.stat_notify_image)  
  144.             .setWhen(System.currentTimeMillis());  
  145.   
  146.         mNotificationStyle = new Notification.BigPictureStyle()  
  147.             .bigPicture(preview);  
  148.         mNotificationBuilder.setStyle(mNotificationStyle);  
  149.   
  150.         Notification n = mNotificationBuilder.build();  
  151.         n.flags |= Notification.FLAG_NO_CLEAR;  
  152.         mNotificationManager.notify(nId, n);  
  153.   
  154.         // On the tablet, the large icon makes the notification appear as if it is clickable (and  
  155.         // on small devices, the large icon is not shown) so defer showing the large icon until  
  156.         // we compose the final post-save notification below.  
  157.         mNotificationBuilder.setLargeIcon(croppedIcon);  
  158.         // But we still don't set it for the expanded view, allowing the smallIcon to show here.  
  159.         mNotificationStyle.bigLargeIcon(null);  
  160.     }  
  161.   
  162.     @Override  
  163.     protected SaveImageInBackgroundData doInBackground(SaveImageInBackgroundData... params) {  
  164.         if (params.length != 1return null;  
  165.   
  166.         // By default, AsyncTask sets the worker thread to have background thread priority, so bump  
  167.         // it back up so that we save a little quicker.  
  168.         Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);  
  169.   
  170.         Context context = params[0].context;  
  171.         Bitmap image = params[0].image;  
  172.         Resources r = context.getResources();  
  173.   
  174.         try {  
  175.             // Save the screenshot to the MediaStore  
  176.             ContentValues values = new ContentValues();  
  177.             ContentResolver resolver = context.getContentResolver();  
  178.             values.put(MediaStore.Images.ImageColumns.DATA, mImageFilePath);  
  179.             values.put(MediaStore.Images.ImageColumns.TITLE, mImageFileName);  
  180.             values.put(MediaStore.Images.ImageColumns.DISPLAY_NAME, mImageFileName);  
  181.             values.put(MediaStore.Images.ImageColumns.DATE_TAKEN, mImageTime);  
  182.             values.put(MediaStore.Images.ImageColumns.DATE_ADDED, mImageTime);  
  183.             values.put(MediaStore.Images.ImageColumns.DATE_MODIFIED, mImageTime);  
  184.             values.put(MediaStore.Images.ImageColumns.MIME_TYPE, "image/png");  
  185.             Uri uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);  
  186.   
  187.             Intent sharingIntent = new Intent(Intent.ACTION_SEND);  
  188.             sharingIntent.setType("image/png");  
  189.             sharingIntent.putExtra(Intent.EXTRA_STREAM, uri);  
  190.   
  191.             Intent chooserIntent = Intent.createChooser(sharingIntent, null);  
  192.             chooserIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK   
  193.                     | Intent.FLAG_ACTIVITY_NEW_TASK);  
  194.   
  195.             mNotificationBuilder.addAction(R.drawable.ic_menu_share,  
  196.                      r.getString(com.android.internal.R.string.share),  
  197.                      PendingIntent.getActivity(context, 0, chooserIntent,   
  198.                              PendingIntent.FLAG_CANCEL_CURRENT));  
  199.   
  200.             OutputStream out = resolver.openOutputStream(uri);  
  201.             image.compress(Bitmap.CompressFormat.PNG, 100, out);  
  202.             out.flush();  
  203.             out.close();  
  204.   
  205.             // update file size in the database  
  206.             values.clear();  
  207.             values.put(MediaStore.Images.ImageColumns.SIZE, new File(mImageFilePath).length());  
  208.             resolver.update(uri, values, nullnull);  
  209.   
  210.             params[0].imageUri = uri;  
  211.             params[0].result = 0;  
  212.         } catch (Exception e) {  
  213.             // IOException/UnsupportedOperationException may be thrown if external storage is not  
  214.             // mounted  
  215.             params[0].result = 1;  
  216.         }  
  217.   
  218.         return params[0];  
  219.     }  
  220.   
  221.     @Override  
  222.     protected void onPostExecute(SaveImageInBackgroundData params) {  
  223.         if (params.result > 0) {  
  224.             // Show a message that we've failed to save the image to disk  
  225.             GlobalScreenshot.notifyScreenshotError(params.context, mNotificationManager);  
  226.         } else {  
  227.             // Show the final notification to indicate screenshot saved  
  228.             Resources r = params.context.getResources();  
  229.   
  230.             // Create the intent to show the screenshot in gallery  
  231.             Intent launchIntent = new Intent(Intent.ACTION_VIEW);  
  232.             launchIntent.setDataAndType(params.imageUri, "image/png");  
  233.             launchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
  234.   
  235.             mNotificationBuilder  
  236.                 .setContentTitle(r.getString(R.string.screenshot_saved_title))  
  237.                 .setContentText(r.getString(R.string.screenshot_saved_text))  
  238.                 .setContentIntent(PendingIntent.getActivity(params.context, 0, launchIntent, 0))  
  239.                 .setWhen(System.currentTimeMillis())  
  240.                 .setAutoCancel(true);  
  241.   
  242.             Notification n = mNotificationBuilder.build();  
  243.             n.flags &= ~Notification.FLAG_NO_CLEAR;  
  244.             mNotificationManager.notify(mNotificationId, n);  
  245.         }  
  246.         params.finisher.run();  
  247.     }  
  248. }  
  249.   
  250. /** 
  251.  * TODO: 
  252.  *   - Performance when over gl surfaces? Ie. Gallery 
  253.  *   - what do we say in the Toast? Which icon do we get if the user uses another 
  254.  *     type of gallery? 
  255.  */  
  256. class GlobalScreenshot {  
  257.     private static final int SCREENSHOT_NOTIFICATION_ID = 789;  
  258.     private static final int SCREENSHOT_FLASH_TO_PEAK_DURATION = 130;  
  259.     private static final int SCREENSHOT_DROP_IN_DURATION = 430;  
  260.     private static final int SCREENSHOT_DROP_OUT_DELAY = 500;  
  261.     private static final int SCREENSHOT_DROP_OUT_DURATION = 430;  
  262.     private static final int SCREENSHOT_DROP_OUT_SCALE_DURATION = 370;  
  263.     private static final int SCREENSHOT_FAST_DROP_OUT_DURATION = 320;  
  264.     private static final float BACKGROUND_ALPHA = 0.5f;  
  265.     private static final float SCREENSHOT_SCALE = 1f;  
  266.     private static final float SCREENSHOT_DROP_IN_MIN_SCALE = SCREENSHOT_SCALE * 0.725f;  
  267.     private static final float SCREENSHOT_DROP_OUT_MIN_SCALE = SCREENSHOT_SCALE * 0.45f;  
  268.     private static final float SCREENSHOT_FAST_DROP_OUT_MIN_SCALE = SCREENSHOT_SCALE * 0.6f;  
  269.     private static final float SCREENSHOT_DROP_OUT_MIN_SCALE_OFFSET = 0f;  
  270.   
  271.     private Context mContext;  
  272.     private WindowManager mWindowManager;  
  273.     private WindowManager.LayoutParams mWindowLayoutParams;  
  274.     private NotificationManager mNotificationManager;  
  275.     private Display mDisplay;  
  276.     private DisplayMetrics mDisplayMetrics;  
  277.     private Matrix mDisplayMatrix;  
  278.   
  279.     private Bitmap mScreenBitmap;  
  280.     private View mScreenshotLayout;  
  281.     private ImageView mBackgroundView;  
  282.     private ImageView mScreenshotView;  
  283.     private ImageView mScreenshotFlash;  
  284.   
  285.     private AnimatorSet mScreenshotAnimation;  
  286.   
  287.     private int mNotificationIconSize;  
  288.     private float mBgPadding;  
  289.     private float mBgPaddingScale;  
  290.   
  291.     private MediaActionSound mCameraSound;  
  292.   
  293.   
  294.     /** 
  295.      * @param context everything needs a context :( 
  296.      */  
  297.     public GlobalScreenshot(Context context) {  
  298.         Resources r = context.getResources();  
  299.         mContext = context;  
  300.         LayoutInflater layoutInflater = (LayoutInflater)  
  301.                 context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);  
  302.   
  303.         // Inflate the screenshot layout  
  304.         mDisplayMatrix = new Matrix();  
  305.         mScreenshotLayout = layoutInflater.inflate(R.layout.global_screenshot, null);  
  306.         mBackgroundView = (ImageView) mScreenshotLayout.findViewById(R.id.global_screenshot_background);  
  307.         mScreenshotView = (ImageView) mScreenshotLayout.findViewById(R.id.global_screenshot);  
  308.         mScreenshotFlash = (ImageView) mScreenshotLayout.findViewById(R.id.global_screenshot_flash);  
  309.         mScreenshotLayout.setFocusable(true);  
  310.         mScreenshotLayout.setOnTouchListener(new View.OnTouchListener() {  
  311.             @Override  
  312.             public boolean onTouch(View v, MotionEvent event) {  
  313.                 // Intercept and ignore all touch events  
  314.                 return true;  
  315.             }  
  316.         });  
  317.   
  318.         // Setup the window that we are going to use  
  319.         mWindowLayoutParams = new WindowManager.LayoutParams(  
  320.                 ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, 00,  
  321.                 WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY,  
  322.                 WindowManager.LayoutParams.FLAG_FULLSCREEN  
  323.                     | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED  
  324.                     | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN  
  325.                     | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED,  
  326.                 PixelFormat.TRANSLUCENT);  
  327.         mWindowLayoutParams.setTitle("ScreenshotAnimation");  
  328.         mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);  
  329.         mNotificationManager =  
  330.             (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);  
  331.         mDisplay = mWindowManager.getDefaultDisplay();  
  332.         mDisplayMetrics = new DisplayMetrics();  
  333.         mDisplay.getRealMetrics(mDisplayMetrics);  
  334.   
  335.         // Get the various target sizes  
  336.         mNotificationIconSize =  
  337.             r.getDimensionPixelSize(android.R.dimen.notification_large_icon_height);  
  338.   
  339.         // Scale has to account for both sides of the bg  
  340.         mBgPadding = (float) r.getDimensionPixelSize(R.dimen.global_screenshot_bg_padding);  
  341.         mBgPaddingScale = mBgPadding /  mDisplayMetrics.widthPixels;  
  342.   
  343.         // Setup the Camera shutter sound  
  344.         mCameraSound = new MediaActionSound();  
  345.         mCameraSound.load(MediaActionSound.SHUTTER_CLICK);  
  346.     }  
  347.   
  348.     /** 
  349.      * Creates a new worker thread and saves the screenshot to the media store. 
  350.      */  
  351.     private void saveScreenshotInWorkerThread(Runnable finisher) {  
  352.         SaveImageInBackgroundData data = new SaveImageInBackgroundData();  
  353.         data.context = mContext;  
  354.         data.image = mScreenBitmap;  
  355.         data.iconSize = mNotificationIconSize;  
  356.         data.finisher = finisher;  
  357.         new SaveImageInBackgroundTask(mContext, data, mNotificationManager,  
  358.                 SCREENSHOT_NOTIFICATION_ID).execute(data);  
  359.     }  
  360.   
  361.     /** 
  362.      * @return the current display rotation in degrees 
  363.      */  
  364.     private float getDegreesForRotation(int value) {  
  365.         switch (value) {  
  366.         case Surface.ROTATION_90:  
  367.             return 360f - 90f;  
  368.         case Surface.ROTATION_180:  
  369.             return 360f - 180f;  
  370.         case Surface.ROTATION_270:  
  371.             return 360f - 270f;  
  372.         }  
  373.         return 0f;  
  374.     }  
  375.   
  376.     /** 
  377.      * Takes a screenshot of the current display and shows an animation. 
  378.      */  
  379.     void takeScreenshot(Runnable finisher, boolean statusBarVisible, boolean navBarVisible) {  
  380.         // We need to orient the screenshot correctly (and the Surface api seems to take screenshots  
  381.         // only in the natural orientation of the device :!)  
  382.         mDisplay.getRealMetrics(mDisplayMetrics);  
  383.         float[] dims = {mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels};  
  384.         float degrees = getDegreesForRotation(mDisplay.getRotation());  
  385.         boolean requiresRotation = (degrees > 0);  
  386.         if (requiresRotation) {  
  387.             // Get the dimensions of the device in its native orientation  
  388.             mDisplayMatrix.reset();  
  389.             mDisplayMatrix.preRotate(-degrees);  
  390.             mDisplayMatrix.mapPoints(dims);  
  391.             dims[0] = Math.abs(dims[0]);  
  392.             dims[1] = Math.abs(dims[1]);  
  393.         }  
  394.   
  395.         // Take the screenshot  
  396.         mScreenBitmap = Surface.screenshot((int) dims[0], (int) dims[1]);  
  397.         if (mScreenBitmap == null) {  
  398.             notifyScreenshotError(mContext, mNotificationManager);  
  399.             finisher.run();  
  400.             return;  
  401.         }  
  402.   
  403.         if (requiresRotation) {  
  404.             // Rotate the screenshot to the current orientation  
  405.             Bitmap ss = Bitmap.createBitmap(mDisplayMetrics.widthPixels,  
  406.                     mDisplayMetrics.heightPixels, Bitmap.Config.ARGB_8888);  
  407.             Canvas c = new Canvas(ss);  
  408.             c.translate(ss.getWidth() / 2, ss.getHeight() / 2);  
  409.             c.rotate(degrees);  
  410.             c.translate(-dims[0] / 2, -dims[1] / 2);  
  411.             c.drawBitmap(mScreenBitmap, 00null);  
  412.             c.setBitmap(null);  
  413.             mScreenBitmap = ss;  
  414.         }  
  415.   
  416.         // Optimizations  
  417.         mScreenBitmap.setHasAlpha(false);  
  418.         mScreenBitmap.prepareToDraw();  
  419.   
  420.         // Start the post-screenshot animation  
  421.         startAnimation(finisher, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels,  
  422.                 statusBarVisible, navBarVisible);  
  423.     }  
  424.   
  425.   
  426.     /** 
  427.      * Starts the animation after taking the screenshot 
  428.      */  
  429.     private void startAnimation(final Runnable finisher, int w, int h, boolean statusBarVisible,  
  430.             boolean navBarVisible) {  
  431.         // Add the view for the animation  
  432.         mScreenshotView.setImageBitmap(mScreenBitmap);  
  433.         mScreenshotLayout.requestFocus();  
  434.   
  435.         // Setup the animation with the screenshot just taken  
  436.         if (mScreenshotAnimation != null) {  
  437.             mScreenshotAnimation.end();  
  438.         }  
  439.   
  440.         mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);  
  441.         ValueAnimator screenshotDropInAnim = createScreenshotDropInAnimation();  
  442.         ValueAnimator screenshotFadeOutAnim = createScreenshotDropOutAnimation(w, h,  
  443.                 statusBarVisible, navBarVisible);  
  444.         mScreenshotAnimation = new AnimatorSet();  
  445.         mScreenshotAnimation.playSequentially(screenshotDropInAnim, screenshotFadeOutAnim);  
  446.         mScreenshotAnimation.addListener(new AnimatorListenerAdapter() {  
  447.             @Override  
  448.             public void onAnimationEnd(Animator animation) {  
  449.                 // Save the screenshot once we have a bit of time now  
  450.                 saveScreenshotInWorkerThread(finisher);  
  451.                 mWindowManager.removeView(mScreenshotLayout);  
  452.             }  
  453.         });  
  454.         mScreenshotLayout.post(new Runnable() {  
  455.             @Override  
  456.             public void run() {  
  457.                 // Play the shutter sound to notify that we've taken a screenshot  
  458.                 mCameraSound.play(MediaActionSound.SHUTTER_CLICK);  
  459.   
  460.                 mScreenshotView.setLayerType(View.LAYER_TYPE_HARDWARE, null);  
  461.                 mScreenshotView.buildLayer();  
  462.                 mScreenshotAnimation.start();  
  463.             }  
  464.         });  
  465.     }  
  466.     private ValueAnimator createScreenshotDropInAnimation() {  
  467.         final float flashPeakDurationPct = ((float) (SCREENSHOT_FLASH_TO_PEAK_DURATION)  
  468.                 / SCREENSHOT_DROP_IN_DURATION);  
  469.         final float flashDurationPct = 2f * flashPeakDurationPct;  
  470.         final Interpolator flashAlphaInterpolator = new Interpolator() {  
  471.             @Override  
  472.             public float getInterpolation(float x) {  
  473.                 // Flash the flash view in and out quickly  
  474.                 if (x <= flashDurationPct) {  
  475.                     return (float) Math.sin(Math.PI * (x / flashDurationPct));  
  476.                 }  
  477.                 return 0;  
  478.             }  
  479.         };  
  480.         final Interpolator scaleInterpolator = new Interpolator() {  
  481.             @Override  
  482.             public float getInterpolation(float x) {  
  483.                 // We start scaling when the flash is at it's peak  
  484.                 if (x < flashPeakDurationPct) {  
  485.                     return 0;  
  486.                 }  
  487.                 return (x - flashDurationPct) / (1f - flashDurationPct);  
  488.             }  
  489.         };  
  490.         ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);  
  491.         anim.setDuration(SCREENSHOT_DROP_IN_DURATION);  
  492.         anim.addListener(new AnimatorListenerAdapter() {  
  493.             @Override  
  494.             public void onAnimationStart(Animator animation) {  
  495.                 mBackgroundView.setAlpha(0f);  
  496.                 mBackgroundView.setVisibility(View.VISIBLE);  
  497.                 mScreenshotView.setAlpha(0f);  
  498.                 mScreenshotView.setTranslationX(0f);  
  499.                 mScreenshotView.setTranslationY(0f);  
  500.                 mScreenshotView.setScaleX(SCREENSHOT_SCALE + mBgPaddingScale);  
  501.                 mScreenshotView.setScaleY(SCREENSHOT_SCALE + mBgPaddingScale);  
  502.                 mScreenshotView.setVisibility(View.VISIBLE);  
  503.                 mScreenshotFlash.setAlpha(0f);  
  504.                 mScreenshotFlash.setVisibility(View.VISIBLE);  
  505.             }  
  506.             @Override  
  507.             public void onAnimationEnd(android.animation.Animator animation) {  
  508.                 mScreenshotFlash.setVisibility(View.GONE);  
  509.             }  
  510.         });  
  511.         anim.addUpdateListener(new AnimatorUpdateListener() {  
  512.             @Override  
  513.             public void onAnimationUpdate(ValueAnimator animation) {  
  514.                 float t = (Float) animation.getAnimatedValue();  
  515.                 float scaleT = (SCREENSHOT_SCALE + mBgPaddingScale)  
  516.                     - scaleInterpolator.getInterpolation(t)  
  517.                         * (SCREENSHOT_SCALE - SCREENSHOT_DROP_IN_MIN_SCALE);  
  518.                 mBackgroundView.setAlpha(scaleInterpolator.getInterpolation(t) * BACKGROUND_ALPHA);  
  519.                 mScreenshotView.setAlpha(t);  
  520.                 mScreenshotView.setScaleX(scaleT);  
  521.                 mScreenshotView.setScaleY(scaleT);  
  522.                 mScreenshotFlash.setAlpha(flashAlphaInterpolator.getInterpolation(t));  
  523.             }  
  524.         });  
  525.         return anim;  
  526.     }  
  527.     private ValueAnimator createScreenshotDropOutAnimation(int w, int h, boolean statusBarVisible,  
  528.             boolean navBarVisible) {  
  529.         ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);  
  530.         anim.setStartDelay(SCREENSHOT_DROP_OUT_DELAY);  
  531.         anim.addListener(new AnimatorListenerAdapter() {  
  532.             @Override  
  533.             public void onAnimationEnd(Animator animation) {  
  534.                 mBackgroundView.setVisibility(View.GONE);  
  535.                 mScreenshotView.setVisibility(View.GONE);  
  536.                 mScreenshotView.setLayerType(View.LAYER_TYPE_NONE, null);  
  537.             }  
  538.         });  
  539.   
  540.         if (!statusBarVisible || !navBarVisible) {  
  541.             // There is no status bar/nav bar, so just fade the screenshot away in place  
  542.             anim.setDuration(SCREENSHOT_FAST_DROP_OUT_DURATION);  
  543.             anim.addUpdateListener(new AnimatorUpdateListener() {  
  544.                 @Override  
  545.                 public void onAnimationUpdate(ValueAnimator animation) {  
  546.                     float t = (Float) animation.getAnimatedValue();  
  547.                     float scaleT = (SCREENSHOT_DROP_IN_MIN_SCALE + mBgPaddingScale)  
  548.                             - t * (SCREENSHOT_DROP_IN_MIN_SCALE - SCREENSHOT_FAST_DROP_OUT_MIN_SCALE);  
  549.                     mBackgroundView.setAlpha((1f - t) * BACKGROUND_ALPHA);  
  550.                     mScreenshotView.setAlpha(1f - t);  
  551.                     mScreenshotView.setScaleX(scaleT);  
  552.                     mScreenshotView.setScaleY(scaleT);  
  553.                 }  
  554.             });  
  555.         } else {  
  556.             // In the case where there is a status bar, animate to the origin of the bar (top-left)  
  557.             final float scaleDurationPct = (float) SCREENSHOT_DROP_OUT_SCALE_DURATION  
  558.                     / SCREENSHOT_DROP_OUT_DURATION;  
  559.             final Interpolator scaleInterpolator = new Interpolator() {  
  560.                 @Override  
  561.                 public float getInterpolation(float x) {  
  562.                     if (x < scaleDurationPct) {  
  563.                         // Decelerate, and scale the input accordingly  
  564.                         return (float) (1f - Math.pow(1f - (x / scaleDurationPct), 2f));  
  565.                     }  
  566.                     return 1f;  
  567.                 }  
  568.             };  
  569.   
  570.             // Determine the bounds of how to scale  
  571.             float halfScreenWidth = (w - 2f * mBgPadding) / 2f;  
  572.             float halfScreenHeight = (h - 2f * mBgPadding) / 2f;  
  573.             final float offsetPct = SCREENSHOT_DROP_OUT_MIN_SCALE_OFFSET;  
  574.             final PointF finalPos = new PointF(  
  575.                 -halfScreenWidth + (SCREENSHOT_DROP_OUT_MIN_SCALE + offsetPct) * halfScreenWidth,  
  576.                 -halfScreenHeight + (SCREENSHOT_DROP_OUT_MIN_SCALE + offsetPct) * halfScreenHeight);  
  577.   
  578.             // Animate the screenshot to the status bar  
  579.             anim.setDuration(SCREENSHOT_DROP_OUT_DURATION);  
  580.             anim.addUpdateListener(new AnimatorUpdateListener() {  
  581.                 @Override  
  582.                 public void onAnimationUpdate(ValueAnimator animation) {  
  583.                     float t = (Float) animation.getAnimatedValue();  
  584.                     float scaleT = (SCREENSHOT_DROP_IN_MIN_SCALE + mBgPaddingScale)  
  585.                         - scaleInterpolator.getInterpolation(t)  
  586.                             * (SCREENSHOT_DROP_IN_MIN_SCALE - SCREENSHOT_DROP_OUT_MIN_SCALE);  
  587.                     mBackgroundView.setAlpha((1f - t) * BACKGROUND_ALPHA);  
  588.                     mScreenshotView.setAlpha(1f - scaleInterpolator.getInterpolation(t));  
  589.                     mScreenshotView.setScaleX(scaleT);  
  590.                     mScreenshotView.setScaleY(scaleT);  
  591.                     mScreenshotView.setTranslationX(t * finalPos.x);  
  592.                     mScreenshotView.setTranslationY(t * finalPos.y);  
  593.                 }  
  594.             });  
  595.         }  
  596.         return anim;  
  597.     }  
  598.   
  599.     static void notifyScreenshotError(Context context, NotificationManager nManager) {  
  600.         Resources r = context.getResources();  
  601.   
  602.         // Clear all existing notification, compose the new notification and show it  
  603.         Notification n = new Notification.Builder(context)  
  604.             .setTicker(r.getString(R.string.screenshot_failed_title))  
  605.             .setContentTitle(r.getString(R.string.screenshot_failed_title))  
  606.             .setContentText(r.getString(R.string.screenshot_failed_text))  
  607.             .setSmallIcon(R.drawable.stat_notify_image_error)  
  608.             .setWhen(System.currentTimeMillis())  
  609.             .setAutoCancel(true)  
  610.             .getNotification();  
  611.         nManager.notify(SCREENSHOT_NOTIFICATION_ID, n);  
  612.     }  

可以从这部分源码中,去寻找解决办法。

其中有一个很重要的方法就是        

mScreenBitmap = Surface.screenshot((int) dims[0], (int) dims[1]);

这个是可以截图的,但是是隐藏的代码,不提供外面调用的。

注意需要在AndroidManifest.xml中加入代码:android:sharedUserId="android.uid.system"  进行后续开发。

先到这里,有时间后续再从这里往下研究。各位也可以yanjiuyxia

本文demo下载地址:http://download.csdn.net/detail/qq_16064871/9420810

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/huningjun/article/details/52326858

智能推荐

过河卒_戴习为 哪里人-程序员宅基地

文章浏览阅读596次。过河卒_戴习为 哪里人

SpringBoot+MyBatis+MySQL电脑商城项目(用户收获管理)_springboot+mybatis新增收货地址-程序员宅基地

文章浏览阅读389次。在mapper包下创建DistrictMapper接口/*** 根据父代号查询区域信息* @return*/}_springboot+mybatis新增收货地址

行遍历和列遍历-程序员宅基地

文章浏览阅读359次,点赞5次,收藏7次。因此,行遍历在性能上通常优于列遍历,尤其是在处理大型二维数组或矩阵时。在遍历过程中,首先访问第一行的所有元素,然后移动到第二行,以此类推,直到遍历完最后一行的所有元素。在遍历过程中,首先访问第一列的所有元素,然后移动到第二列,以此类推,直到遍历完最后一列的所有元素。行遍历通常意味着连续的内存访问,因为二维数组在内存中是按照行优先的顺序存储的。列遍历则可能导致非连续的内存访问,因为需要跨越不同行来访问同一列的元素。行遍历是按行顺序访问元素,而列遍历是按列顺序访问元素。可以在下一章节看看。

springBoot--maven打包跳过测试_pom设置springboot打包跳过test-程序员宅基地

文章浏览阅读3.1k次。在pom.xml中 添加<skipTests>true</skipTests>如下<!--版本管理--> <properties> <skipTests>true</skipTests> <java.version>1.8</java.versio..._pom设置springboot打包跳过test

java基础之IO流_cannot invoke "java.io.file.equals(object)" becaus-程序员宅基地

文章浏览阅读136次。1、IO流结构IO流 字节流 1、直接操作文件,操作二进制文件速度更快 2、以字节为单位按8位传输 InputStream字节输入流 处理流 ObjectInputStream FilterInputStream BufferedInputStream DataInputStream 节点流 FileInputStream ByteArrayInputStream OutputStream字节输出流 ..._cannot invoke "java.io.file.equals(object)" because "current" is null

springboot高校体育场馆预约管理系统设计与实现_2m1zl[独有源码]了解毕业设计的关键考虑因素_学校体育馆预约管理可行性报告分析-程序员宅基地

文章浏览阅读633次,点赞2次,收藏12次。选题背景:随着社会经济的发展和人们生活水平的提高,大学生对体育锻炼的需求越来越高。而高校体育场馆作为提供体育锻炼场所的重要设施,承担着满足学生体育需求的重要责任。然而,目前很多高校体育场馆预约管理仍然采用传统的人工方式,存在着预约效率低、信息不透明、资源浪费等问题。因此,设计和实现一套高校体育场馆预约管理系统具有重要的现实意义。选题意义:首先,高校体育场馆预约管理系统的设计与实现可以提高预约效率。传统的人工预约方式需要学生亲自到场馆进行预约,耗费时间和精力。而通过系统化的预约管理,学生可以随时随地通_学校体育馆预约管理可行性报告分析

随便推点

【UE4/蓝图/C++】简易FPS武器视角随动效果-程序员宅基地

文章浏览阅读820次,点赞24次,收藏11次。制作了一个简单的第一人称的手臂跟随相机反方向偏移的功能,分别介绍了用蓝图和C++进行实现

# nest笔记八:使用apifox导入swagger_apifox swagger-程序员宅基地

文章浏览阅读1.5k次,点赞2次,收藏4次。- apifox是一个很不错的类postman工具,除了它国内还有不少类似的工具,我一个偶然的机会,就用它了, 目前使用来看,还不错。- nestjs提供了对swagger的支持,我们只要按它的定义,就可以了- nestjs的官方文档:https://docs.nestjs.com/openapi/introduction..._apifox swagger

【c语言】函数的默认参数_c语言默认参数-程序员宅基地

文章浏览阅读1.4w次,点赞10次,收藏20次。在c语言中是没有函数默认值的概念,可以利用宏来模拟参数默认值;在c++中可以为参数指定默认值;所谓函数默认值就是当调用点没有相对应的形参时,就自动使用默认参数,编译器会自动把默认值传递给调用语句中;设置函数默认值需要注意有以下几点1.函数默认值一般写在声明中2.函数的默认值必须设置为自右向左依次赋值3.默认值只能赋一次4.函数的默认值不能设置为局部变量第一点:先写一段简..._c语言默认参数

ssm023实验室耗材管理系统设计与实现+jsp-程序员宅基地

文章浏览阅读839次,点赞26次,收藏16次。本课题所开发的应用程序在数据操作方面是不可预知的,是经常变动的,没有办法直接把数据写在文档里,这样不仅仅不安全,也不能实现应用程序的功能。参考自己的学习进度和操作习惯来讲,Oracle数据库是适合的,但是所需要的的安装软件很大,并且有好多不需要的功能都是开启的状态,十分消耗电脑资源,所以没有选择Oracle数据库,而SQL Server数据库虽然学过,但是安装的时候因为电脑上可能有其他的软件存在,经常性的出问题,而安装问题不好解决就需要重新安装操作系统,这样对已经存在的软件来讲又是一种时间上的浪费。

Altium Designer(五)光耦元件模型的创建_ad16怎么给元件添加模型-程序员宅基地

文章浏览阅读2.2k次,点赞5次,收藏3次。Altium Designer(五)光耦元件模型的创建软件:Altium Designer 161、点击打开“原理图库”,点击右下角的【SCH】——>【SCH LIbrary】2、若无法找到【SCH】,则点击【察看】——>【状态栏】,右下角即会显示【SCH】3、在【SCH Library】栏点击添加,并改名为“HCPL0631”4、画二极管,使用画多边形的工具画二极管。5、点击三角形设置填充颜色、边框宽度。将边框宽度调到最低可以使三角形的外框更尖锐6、调节横线的线_ad16怎么给元件添加模型

TEC简介_tec csdn-程序员宅基地

文章浏览阅读458次,点赞7次,收藏7次。所谓珀尔帖效应,是指当直流电流通过两种半导体材料组成的电偶时,在电偶的两端即可出现一端吸收热量,一端放出热量的现象。TEC具有无噪音、无振动、无需制冷剂、体积小、重量轻等特点,工作可靠,操作方便,制冷制热快,调节方便,温度控制精确。但是,它的制冷系数相对较小,功耗相对较大,因此主要用于冷耗小,占地面积小的场合,例如电子设备和无线电通信设备中某些组件的冷却。4、最大温差:小型半导体制冷片TEC的最大温差是衡量其制冷能力的指标,通常在几摄氏度到几十摄氏度之间。2、温度变化会引起SOA芯片增益谱的变化。_tec csdn

推荐文章

热门文章

相关标签