1 /*
2  * Copyright (C) 2020 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 package com.android.wallpaper.util
17 
18 import android.content.ContentResolver
19 import android.content.Context
20 import android.content.Intent
21 import android.content.pm.PackageManager
22 import android.content.pm.ProviderInfo
23 import android.net.Uri
24 import android.os.Bundle
25 import android.os.Handler
26 import android.os.Looper
27 import android.os.Message
28 import android.text.TextUtils
29 import java.util.concurrent.Executors
30 
31 /** Util class for wallpaper preview. */
32 class PreviewUtils(
33     private val context: Context,
34     authorityMetadataKey: String? = null,
35     authority: String? = null,
36 ) {
37     /** Callback for a call to the provider to render preview */
38     interface WorkspacePreviewCallback {
39         /** Called with the result from the provider. */
onPreviewRenderednull40         fun onPreviewRendered(resultBundle: Bundle?)
41     }
42 
43     private var providerInfo: ProviderInfo?
44 
45     constructor(
46         context: Context,
47         authorityMetadataKey: String,
48     ) : this(
49         context = context.applicationContext,
50         authorityMetadataKey = authorityMetadataKey,
51         authority = null,
52     )
53 
54     init {
55         val providerAuthority =
56             authority ?: homeAuthority(context, checkNotNull(authorityMetadataKey)) ?: ""
57 
58         providerInfo =
59             if (!TextUtils.isEmpty(providerAuthority)) {
60                 context.packageManager.resolveContentProvider(
61                     providerAuthority,
62                     0,
63                 )
64             } else {
65                 null
66             }
67 
68         providerInfo?.let {
69             if (!TextUtils.isEmpty(it.readPermission)) {
70                 if (
71                     context.checkSelfPermission(it.readPermission) !=
72                         PackageManager.PERMISSION_GRANTED
73                 ) {
74                     providerInfo = null
75                 }
76             }
77         }
78     }
79 
80     /**
81      * Render preview under the current grid option.
82      *
83      * @param bundle request options to pass on the call.
84      * @param callback to receive the results, it will be called on the main thread.
85      */
renderPreviewnull86     fun renderPreview(bundle: Bundle?, callback: WorkspacePreviewCallback) {
87         EXECUTOR_SERVICE.submit {
88             val result =
89                 context.contentResolver.call(
90                     getUri(PREVIEW),
91                     METHOD_GET_PREVIEW,
92                     null,
93                     bundle,
94                 )
95             Handler(Looper.getMainLooper()).post { callback.onPreviewRendered(result) }
96         }
97     }
98 
99     /** Cleans up the preview on the renderer side */
cleanUpnull100     fun cleanUp(workspaceCallback: Message?) {
101         // Send any message to clean up the corresponding preview on the renderer side.
102         workspaceCallback?.replyTo?.send(workspaceCallback)
103     }
104 
105     /** Easy way to generate a Uri with the provider info from this class. */
getUrinull106     fun getUri(path: String?): Uri {
107         return Uri.Builder()
108             .scheme(ContentResolver.SCHEME_CONTENT)
109             .authority(checkNotNull(providerInfo).authority)
110             .appendPath(path)
111             .build()
112     }
113 
114     /** Return whether preview is supported. */
supportsPreviewnull115     fun supportsPreview(): Boolean {
116         return providerInfo != null
117     }
118 
119     companion object {
120         private const val PREVIEW = "preview"
121         private const val METHOD_GET_PREVIEW = "get_preview"
122         private val EXECUTOR_SERVICE = Executors.newSingleThreadExecutor()
123 
homeAuthoritynull124         private fun homeAuthority(context: Context, authorityMetadataKey: String): String? {
125             val homeIntent = Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME)
126             val info =
127                 context.packageManager.resolveActivity(
128                     homeIntent,
129                     PackageManager.MATCH_DEFAULT_ONLY or PackageManager.GET_META_DATA,
130                 )
131 
132             return info?.activityInfo?.metaData?.getString(authorityMetadataKey)
133         }
134     }
135 }
136