1 /*
2  * Copyright (C) 2023 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.systemui.clipboardoverlay
17 
18 import android.content.ClipData
19 import android.content.ClipDescription.EXTRA_IS_SENSITIVE
20 import android.content.Context
21 import android.graphics.Bitmap
22 import android.net.Uri
23 import android.text.TextUtils
24 import android.util.Log
25 import android.util.Size
26 import android.view.textclassifier.TextLinks
27 import com.android.systemui.res.R
28 import java.io.IOException
29 
30 data class ClipboardModel(
31     val clipData: ClipData,
32     val source: String,
33     val type: Type,
34     val text: CharSequence?,
35     val textLinks: TextLinks?,
36     val uri: Uri?,
37     val isSensitive: Boolean,
38     val isRemote: Boolean,
39 ) {
40     private var _bitmap: Bitmap? = null
41 
dataMatchesnull42     fun dataMatches(other: ClipboardModel?): Boolean {
43         if (other == null) {
44             return false
45         }
46         return source == other.source &&
47             type == other.type &&
48             text == other.text &&
49             uri == other.uri &&
50             isSensitive == other.isSensitive
51     }
52 
loadThumbnailnull53     fun loadThumbnail(context: Context): Bitmap? {
54         if (_bitmap == null && type == Type.IMAGE && uri != null) {
55             try {
56                 val size = context.resources.getDimensionPixelSize(R.dimen.overlay_x_scale)
57                 _bitmap = context.contentResolver.loadThumbnail(uri, Size(size, size * 4), null)
58             } catch (e: IOException) {
59                 Log.e(TAG, "Thumbnail loading failed!", e)
60             }
61         }
62         return _bitmap
63     }
64 
65     internal companion object {
66         private val TAG: String = "ClipboardModel"
67 
68         @JvmStatic
fromClipDatanull69         fun fromClipData(
70             context: Context,
71             utils: ClipboardOverlayUtils,
72             clipData: ClipData,
73             source: String
74         ): ClipboardModel {
75             val sensitive = clipData.description?.extras?.getBoolean(EXTRA_IS_SENSITIVE) ?: false
76             val item = clipData.getItemAt(0)!!
77             val type = getType(context, item)
78             val remote = utils.isRemoteCopy(context, clipData, source)
79             return ClipboardModel(
80                 clipData,
81                 source,
82                 type,
83                 item.text,
84                 item.textLinks,
85                 item.uri,
86                 sensitive,
87                 remote
88             )
89         }
90 
getTypenull91         private fun getType(context: Context, item: ClipData.Item): Type {
92             return if (!TextUtils.isEmpty(item.text)) {
93                 Type.TEXT
94             } else if (item.uri != null) {
95                 if (context.contentResolver.getType(item.uri)?.startsWith("image") == true) {
96                     Type.IMAGE
97                 } else {
98                     Type.URI
99                 }
100             } else {
101                 Type.OTHER
102             }
103         }
104     }
105 
106     enum class Type {
107         TEXT,
108         IMAGE,
109         URI,
110         OTHER
111     }
112 }
113