1 /*
2  * Copyright (C) 2022 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.content.ClipData
20 import android.content.ClipDescription
21 import android.content.ComponentName
22 import android.content.ContentProvider
23 import android.content.Context
24 import android.content.Intent
25 import android.net.Uri
26 import com.android.systemui.R
27 
28 object ActionIntentCreator {
29     /** @return a chooser intent to share the given URI. */
30     fun createShare(uri: Uri): Intent = createShare(uri, subject = null, text = null)
31 
32     /** @return a chooser intent to share the given URI with the optional provided subject. */
33     fun createShareWithSubject(uri: Uri, subject: String): Intent =
34         createShare(uri, subject = subject)
35 
36     /** @return a chooser intent to share the given URI with the optional provided extra text. */
37     fun createShareWithText(uri: Uri, extraText: String): Intent =
38         createShare(uri, text = extraText)
39 
40     private fun createShare(rawUri: Uri, subject: String? = null, text: String? = null): Intent {
41         val uri = uriWithoutUserId(rawUri)
42 
43         // Create a share intent, this will always go through the chooser activity first
44         // which should not trigger auto-enter PiP
45         val sharingIntent =
46             Intent(Intent.ACTION_SEND).apply {
47                 setDataAndType(uri, "image/png")
48                 putExtra(Intent.EXTRA_STREAM, uri)
49 
50                 // Include URI in ClipData also, so that grantPermission picks it up.
51                 // We don't use setData here because some apps interpret this as "to:".
52                 clipData =
53                     ClipData(
54                         ClipDescription("content", arrayOf(ClipDescription.MIMETYPE_TEXT_PLAIN)),
55                         ClipData.Item(uri)
56                     )
57 
58                 subject?.let { putExtra(Intent.EXTRA_SUBJECT, subject) }
59                 text?.let { putExtra(Intent.EXTRA_TEXT, text) }
60                 addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
61                 addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
62             }
63 
64         return Intent.createChooser(sharingIntent, null)
65             .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
66             .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
67             .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
68     }
69 
70     /**
71      * @return an ACTION_EDIT intent for the given URI, directed to config_screenshotEditor if
72      *   available.
73      */
74     fun createEdit(rawUri: Uri, context: Context): Intent {
75         val uri = uriWithoutUserId(rawUri)
76         val editIntent = Intent(Intent.ACTION_EDIT)
77 
78         val editor = context.getString(R.string.config_screenshotEditor)
79         if (editor.isNotEmpty()) {
80             editIntent.component = ComponentName.unflattenFromString(editor)
81         }
82 
83         return editIntent
84             .setDataAndType(uri, "image/png")
85             .putExtra(EXTRA_EDIT_SOURCE, EDIT_SOURCE_SCREENSHOT)
86             .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
87             .addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
88             .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
89             .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
90     }
91 
92     private const val EXTRA_EDIT_SOURCE = "edit_source"
93     private const val EDIT_SOURCE_SCREENSHOT = "screenshot"
94 }
95 
96 /**
97  * URIs here are passed only via Intent which are sent to the target user via Intent. Because of
98  * this, the userId component can be removed to prevent compatibility issues when an app attempts
99  * valid a URI containing a userId within the authority.
100  */
101 private fun uriWithoutUserId(uri: Uri): Uri {
102     return ContentProvider.getUriWithoutUserId(uri)
103 }
104