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