1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
|
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.printspooler.model;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.drawable.Icon;
import android.print.PrinterId;
import android.util.Log;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.SortedMap;
import java.util.TreeMap;
/**
* A fixed size cache for custom printer icons. Old icons get removed with a last recently used
* policy.
*/
public class CustomPrinterIconCache {
private final static String LOG_TAG = "CustomPrinterIconCache";
/** Maximum number of icons in the cache */
private final static int MAX_SIZE = 1024;
/** Directory used to persist state and icons */
private final File mCacheDirectory;
/**
* Create a new icon cache.
*/
public CustomPrinterIconCache(@NonNull File cacheDirectory) {
mCacheDirectory = new File(cacheDirectory, "icons");
if (!mCacheDirectory.exists()) {
mCacheDirectory.mkdir();
}
}
/**
* Return the file name to be used for the icon of a printer
*
* @param printerId the id of the printer
*
* @return The file to be used for the icon of the printer
*/
private @Nullable File getIconFileName(@NonNull PrinterId printerId) {
StringBuffer sb = new StringBuffer(printerId.getServiceName().getPackageName());
sb.append("-");
try {
MessageDigest md = MessageDigest.getInstance("SHA-1");
md.update(
(printerId.getServiceName().getClassName() + ":" + printerId.getLocalId())
.getBytes("UTF-16"));
sb.append(String.format("%#040x", new java.math.BigInteger(1, md.digest())));
} catch (UnsupportedEncodingException|NoSuchAlgorithmException e) {
Log.e(LOG_TAG, "Could not compute custom printer icon file name", e);
return null;
}
return new File(mCacheDirectory, sb.toString());
}
/**
* Get the {@link Icon} to be used as a custom icon for the printer. If not available request
* the icon to be loaded.
*
* @param printerId the printer the icon belongs to
* @return the {@link Icon} if already available or null if icon is not loaded yet
*/
public synchronized @Nullable Icon getIcon(@NonNull PrinterId printerId) {
Icon icon;
File iconFile = getIconFileName(printerId);
if (iconFile != null && iconFile.exists()) {
try (FileInputStream is = new FileInputStream(iconFile)) {
icon = Icon.createFromStream(is);
} catch (IOException e) {
icon = null;
Log.e(LOG_TAG, "Could not read icon from " + iconFile, e);
}
// Touch file so that it is the not likely to be removed
iconFile.setLastModified(System.currentTimeMillis());
} else {
icon = null;
}
return icon;
}
/**
* Remove old icons so that only between numFilesToKeep and twice as many icons are left.
*
* @param numFilesToKeep the number of icons to keep
*/
public void removeOldFiles(int numFilesToKeep) {
File files[] = mCacheDirectory.listFiles();
// To reduce the number of shrink operations, let the cache grow to twice the max size
if (files.length > numFilesToKeep * 2) {
SortedMap<Long, File> sortedFiles = new TreeMap<>();
for (File f : files) {
sortedFiles.put(f.lastModified(), f);
}
while (sortedFiles.size() > numFilesToKeep) {
sortedFiles.remove(sortedFiles.firstKey());
}
}
}
/**
* Handle that a custom icon for a printer was loaded
*
* @param printerId the id of the printer the icon belongs to
* @param icon the icon that was loaded
*/
public synchronized void onCustomPrinterIconLoaded(@NonNull PrinterId printerId,
@Nullable Icon icon) {
File iconFile = getIconFileName(printerId);
if (iconFile == null) {
return;
}
try (FileOutputStream os = new FileOutputStream(iconFile)) {
icon.writeToStream(os);
} catch (IOException e) {
Log.e(LOG_TAG, "Could not write icon for " + printerId + " to storage", e);
}
removeOldFiles(MAX_SIZE);
}
/**
* Clear all persisted and non-persisted state from this cache.
*/
public synchronized void clear() {
for (File f : mCacheDirectory.listFiles()) {
f.delete();
}
}
}
|