【问题标题】:How can I take and share a screenshot without using the external storage?如何在不使用外部存储的情况下截取和共享屏幕截图?
【发布时间】:2015-07-31 15:31:30
【问题描述】:

我想以编程方式截取屏幕截图并使用 ShareActionProvider 共享它,而无需请求“android.permission.WRITE_EXTERNAL_STORAGE”权限。 我正在尝试这样做,因为我想避免在不是绝对必要的情况下请求权限,并且能够处理没有外部存储的设备。

我成功地将屏幕截图发送到一些应用程序,但至少有一个(Gmail)不会附加我的屏幕截图。 这是我的代码:

...
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    ...
    MenuItem shareMenuItem = menu.findItem(R.id.action_share);
    ShareActionProvider shareActionProvider = (ShareActionProvider) MenuItemCompat.getActionProvider(shareMenuItem);
    shareActionProvider.setShareIntent(createShareIntent());
    shareActionProvider.setOnShareTargetSelectedListener(new ShareActionProvider.OnShareTargetSelectedListener()
    {
        @Override
        public boolean onShareTargetSelected(ShareActionProvider source, Intent intent)
        {
            saveScreenshot();
            // The return result is ignored. Return false for consistency.
            return false;
        }
    });
}

private Intent createShareIntent() {
    // Get the path of the screenshot inside the directory holding application files.
    File screenshotFile = new File(getActivity().getApplicationContext().getFilesDir(), SCREENSHOT_NAME);

    Intent shareIntent = new Intent(Intent.ACTION_SEND);
    shareIntent.setType("image/jpeg");
    shareIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(screenshotFile));
    return shareIntent;
}

private void saveScreenshot() {
    view.setDrawingCacheEnabled(true);
    Bitmap bitmap = Bitmap.createBitmap(view.getDrawingCache());
    view.setDrawingCacheEnabled(false);

    // Save on Internal Storage to avoid asking for WRITE_EXTERNAL_STORAGE permission.
    FileOutputStream outputStream = null;
    try {
        // Open a file associated with this Context's application package for writing.
        // Make file readable by other apps otherwise it can't be shared.
        outputStream = getActivity().getApplicationContext()
                .openFileOutput(SCREENSHOT_NAME, Context.MODE_WORLD_READABLE);
        bitmap.compress(Bitmap.CompressFormat.JPEG, COMPRESS_QUALITY, outputStream);
        outputStream.close();
    } catch (IOException e) {
        Log.e(LOG_TAG, "Can't save screenshot!", e);
    } finally {
        if (outputStream != null) {
            try {
                outputStream.close();
            } catch (IOException e) {
                Log.e(LOG_TAG, "Can't close the output stream!", e);
            }
        }
    }
}
...

如果我想与 Twitter、Hangouts、Keep、Evernote、Google 的 Inbox 和我测试过的其他应用程序共享,此代码有效,但它不适用于 Gmail。 例如,在 Nexus 6 上,Gmail 给了我一条 Toast 消息,上面写着“Permission denied”,而在 Nexus 4 上,它只是不附加屏幕截图而没有任何错误消息。 日志中没有任何相关内容。 使用类似的东西

screenshotFile.setReadable(true, false);

try {
    Runtime.getRuntime().exec("chmod 777 " + screenshotFile.getAbsolutePath());
} catch (IOException e) {
    Log.e(LOG_TAG, "Can't give permissions to screenshot file!", e);
}

没有区别。

如果我使用 Environment.getExternalStorageDirectory() 将屏幕截图保存在外部存储上并请求“android.permission.WRITE_EXTERNAL_STORAGE”权限,我测试的所有应用都可以正常工作.

如何在不使用外部存储的情况下执行此操作并让所有应用(包括 Gmail)正常运行? 这可能是 Gmail 应用程序的问题吗? 或者您会建议我只请求允许在外部存储上写入并接受我的一些用户可能会抱怨并且某些设备没有 SD 卡的事实吗?

谢谢!

【问题讨论】:

  • deal with devices without external storage.。现在所有的设备都有外部存储。

标签: android permissions screenshot


【解决方案1】:

这是您使用 fileprovider 实现您想要的功能所必须做的。我已经在我的应用程序中完成了。

创建一个文件为 res/xml/paths.xml 并添加它

<?xml version="1.0" encoding="utf-8"?>
<paths >
    <cache-path name="shared_images" path="images/"/>
</paths>

然后在你的 AndroidManifest.xml 中声明文件提供者 android 用你的包名替换 android:authorities 属性

<provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="com.harkunwar.testapp.fileprovider"
        <!--put your package name here-->
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/paths" />
            <!--put your xml file name here-->
</provider>

然后用这个方法分享你的Bitmap

void share(Bitmap bitmap) {
    String fileName = "share.png";
    File dir = new File(getCacheDir(), "images");
    dir.mkdirs();
    File file = new File(dir, fileName);
    try {
        FileOutputStream fOut = new FileOutputStream(file);
        bitmap.compress(Bitmap.CompressFormat.PNG, 100, fOut);
        fOut.flush();
        fOut.close();
     } catch (Exception e) {
        e.printStackTrace();
     }
     Uri uri = FileProvider.getUriForFile(this, getPackageName()+".fileprovider", file);

     Intent intent = new Intent();
     intent.setAction(Intent.ACTION_SEND);
     intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
     intent.setType("image/*");
     intent.setDataAndType(uri, getContentResolver().getType(uri));
     intent.putExtra(Intent.EXTRA_SUBJECT, "");
     intent.putExtra(Intent.EXTRA_TEXT, "Here's the shared image");
     intent.putExtra(Intent.EXTRA_STREAM, uri);

      try {
         startActivity(Intent.createChooser(intent, "Share Image"));
      } catch (ActivityNotFoundException e) {
         Toast.makeText(this, "No App Available", Toast.LENGTH_SHORT).show();
      }
}

然后我用这个简单的代码来分享截图,使用前面的函数。

View rootView = getWindow().getDecorView().findViewById(android.R.id.content);
Bitmap screenshot = getScreenShot(rootView);
share(screenshot);

【讨论】:

    【解决方案2】:

    如何在不使用外部存储的情况下做到这一点并使所有应用程序(包括 Gmail)正常运行?

    您可以try FileProvider,可能与my LegacyCompatCursorWrapper一起使用。

    这可能是 Gmail 应用的问题吗?

    我认为这是一项功能,而不是错误。 MODE_WORLD_READABLE 已被弃用一段时间,这是有充分理由的。就目前而言,您的代码使您的屏幕截图可用于所有应用程序,从安全角度来看,这很糟糕。正确使用ContentProvider 可以在有限的时间内限制对一个应用程序(不是全部)、一个请求(这个特定的ACTION_SEND 操作)的访问。或许 Gmail 开发者只是想在正确的方向上帮助其他人。

    有些设备没有 SD 卡?

    External storage大量大多数 Android 设备上不是 removable storage,包括几乎所有搭载 Android 3.0 或更高版本的设备。如果您知道某个设备型号附带了更新的东西,而外部存储设备是可移动卡,请告诉我。

    【讨论】:

    • 我按照@CommonsWare 的建议使用了 FileProvider,包括 Gmail 在内的所有应用程序现在都可以从包含我的应用程序文件的目录中获取屏幕截图。我摆脱了 MODE_WORLD_READABLE。我遵循了here 的说明,只有两个例外: - 我不使用 filepaths.xml 中的路径属性,因为 openFileOutput() 方法不允许使用路径分隔符。 - 我为所有可以处理我的意图的应用程序授予权限,然后我使用 revokeUriPermission() 撤销权限。
    猜你喜欢
    • 1970-01-01
    • 2019-12-06
    • 1970-01-01
    • 2011-10-21
    • 2011-01-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-07
    相关资源
    最近更新 更多