From 449a64979c859fa5929cccdf4db541c2fe31b0b3 Mon Sep 17 00:00:00 2001 From: Adam Koch Date: Wed, 26 Feb 2014 13:56:39 +1100 Subject: [PATCH] Remove bitmapfun code sample (it has moved to samples framework). Change-Id: I70fc6437815001c7b98174cfe2e29e1244c55e75 --- .../training/bitmapfun/BitmapFun/build.gradle | 27 - .../BitmapFun/src/main/AndroidManifest.xml | 55 - .../android/bitmapfun/provider/Images.java | 245 ----- .../bitmapfun/ui/ImageDetailActivity.java | 213 ---- .../bitmapfun/ui/ImageDetailFragment.java | 107 -- .../bitmapfun/ui/ImageGridActivity.java | 45 - .../bitmapfun/ui/ImageGridFragment.java | 332 ------ .../bitmapfun/ui/RecyclingImageView.java | 89 -- .../android/bitmapfun/util/AsyncTask.java | 693 ------------- .../android/bitmapfun/util/DiskLruCache.java | 953 ------------------ .../android/bitmapfun/util/ImageCache.java | 725 ------------- .../android/bitmapfun/util/ImageFetcher.java | 311 ------ .../android/bitmapfun/util/ImageResizer.java | 264 ----- .../android/bitmapfun/util/ImageWorker.java | 477 --------- .../util/RecyclingBitmapDrawable.java | 103 -- .../example/android/bitmapfun/util/Utils.java | 82 -- .../main/res/drawable-hdpi/ic_launcher.png | Bin 4440 -> 0 bytes .../main/res/drawable-ldpi/ic_launcher.png | Bin 2729 -> 0 bytes .../main/res/drawable-mdpi/ic_launcher.png | Bin 2741 -> 0 bytes .../main/res/drawable-nodpi/empty_photo.png | Bin 3159 -> 0 bytes .../main/res/drawable-xhdpi/ic_launcher.png | Bin 6245 -> 0 bytes .../main/res/drawable-xxhdpi/ic_launcher.png | Bin 11128 -> 0 bytes .../res/drawable/photogrid_list_selector.xml | 31 - .../main/res/layout/image_detail_fragment.xml | 34 - .../main/res/layout/image_detail_pager.xml | 23 - .../main/res/layout/image_grid_fragment.xml | 29 - .../BitmapFun/src/main/res/menu/main_menu.xml | 25 - .../src/main/res/values-large/dimens.xml | 23 - .../src/main/res/values-v11/styles.xml | 36 - .../src/main/res/values-xlarge/dimens.xml | 23 - .../BitmapFun/src/main/res/values/colors.xml | 23 - .../BitmapFun/src/main/res/values/dimens.xml | 24 - .../BitmapFun/src/main/res/values/strings.xml | 31 - .../BitmapFun/src/main/res/values/styles.xml | 29 - samples/training/bitmapfun/README | 8 - samples/training/bitmapfun/build.gradle | 1 - .../bitmapfun/local.properties.sample | 7 - samples/training/bitmapfun/settings.gradle | 1 - 38 files changed, 5069 deletions(-) delete mode 100644 samples/training/bitmapfun/BitmapFun/build.gradle delete mode 100644 samples/training/bitmapfun/BitmapFun/src/main/AndroidManifest.xml delete mode 100644 samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/provider/Images.java delete mode 100644 samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/ui/ImageDetailActivity.java delete mode 100644 samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/ui/ImageDetailFragment.java delete mode 100644 samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/ui/ImageGridActivity.java delete mode 100644 samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/ui/ImageGridFragment.java delete mode 100644 samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/ui/RecyclingImageView.java delete mode 100644 samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/AsyncTask.java delete mode 100644 samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/DiskLruCache.java delete mode 100644 samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/ImageCache.java delete mode 100644 samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/ImageFetcher.java delete mode 100644 samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/ImageResizer.java delete mode 100644 samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/ImageWorker.java delete mode 100644 samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/RecyclingBitmapDrawable.java delete mode 100644 samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/Utils.java delete mode 100644 samples/training/bitmapfun/BitmapFun/src/main/res/drawable-hdpi/ic_launcher.png delete mode 100644 samples/training/bitmapfun/BitmapFun/src/main/res/drawable-ldpi/ic_launcher.png delete mode 100644 samples/training/bitmapfun/BitmapFun/src/main/res/drawable-mdpi/ic_launcher.png delete mode 100644 samples/training/bitmapfun/BitmapFun/src/main/res/drawable-nodpi/empty_photo.png delete mode 100644 samples/training/bitmapfun/BitmapFun/src/main/res/drawable-xhdpi/ic_launcher.png delete mode 100644 samples/training/bitmapfun/BitmapFun/src/main/res/drawable-xxhdpi/ic_launcher.png delete mode 100644 samples/training/bitmapfun/BitmapFun/src/main/res/drawable/photogrid_list_selector.xml delete mode 100644 samples/training/bitmapfun/BitmapFun/src/main/res/layout/image_detail_fragment.xml delete mode 100644 samples/training/bitmapfun/BitmapFun/src/main/res/layout/image_detail_pager.xml delete mode 100644 samples/training/bitmapfun/BitmapFun/src/main/res/layout/image_grid_fragment.xml delete mode 100644 samples/training/bitmapfun/BitmapFun/src/main/res/menu/main_menu.xml delete mode 100644 samples/training/bitmapfun/BitmapFun/src/main/res/values-large/dimens.xml delete mode 100644 samples/training/bitmapfun/BitmapFun/src/main/res/values-v11/styles.xml delete mode 100644 samples/training/bitmapfun/BitmapFun/src/main/res/values-xlarge/dimens.xml delete mode 100644 samples/training/bitmapfun/BitmapFun/src/main/res/values/colors.xml delete mode 100644 samples/training/bitmapfun/BitmapFun/src/main/res/values/dimens.xml delete mode 100644 samples/training/bitmapfun/BitmapFun/src/main/res/values/strings.xml delete mode 100644 samples/training/bitmapfun/BitmapFun/src/main/res/values/styles.xml delete mode 100644 samples/training/bitmapfun/README delete mode 100644 samples/training/bitmapfun/build.gradle delete mode 100644 samples/training/bitmapfun/local.properties.sample delete mode 100644 samples/training/bitmapfun/settings.gradle diff --git a/samples/training/bitmapfun/BitmapFun/build.gradle b/samples/training/bitmapfun/BitmapFun/build.gradle deleted file mode 100644 index c9782314d..000000000 --- a/samples/training/bitmapfun/BitmapFun/build.gradle +++ /dev/null @@ -1,27 +0,0 @@ -buildscript { - repositories { - mavenCentral() - } - dependencies { - classpath 'com.android.tools.build:gradle:0.6.+' - } -} -apply plugin: 'android' - -repositories { - mavenCentral() -} - -android { - compileSdkVersion 19 - buildToolsVersion "19.0.0" - - defaultConfig { - minSdkVersion 7 - targetSdkVersion 19 - } -} - -dependencies { - compile 'com.android.support:support-v4:19.0.+' -} diff --git a/samples/training/bitmapfun/BitmapFun/src/main/AndroidManifest.xml b/samples/training/bitmapfun/BitmapFun/src/main/AndroidManifest.xml deleted file mode 100644 index 9ca5cf50c..000000000 --- a/samples/training/bitmapfun/BitmapFun/src/main/AndroidManifest.xml +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/provider/Images.java b/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/provider/Images.java deleted file mode 100644 index fcf4496bd..000000000 --- a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/provider/Images.java +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Copyright (C) 2012 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.example.android.bitmapfun.provider; - -/** - * Some simple test data to use for this sample app. - */ -public class Images { - - /** - * This are PicasaWeb URLs and could potentially change. Ideally the PicasaWeb API should be - * used to fetch the URLs. - * - * Credit to Romain Guy for the photos: - * http://www.curious-creature.org/ - * https://plus.google.com/109538161516040592207/about - * http://www.flickr.com/photos/romainguy - */ - public final static String[] imageUrls = new String[] { - "https://lh6.googleusercontent.com/-55osAWw3x0Q/URquUtcFr5I/AAAAAAAAAbs/rWlj1RUKrYI/s1024/A%252520Photographer.jpg", - "https://lh4.googleusercontent.com/--dq8niRp7W4/URquVgmXvgI/AAAAAAAAAbs/-gnuLQfNnBA/s1024/A%252520Song%252520of%252520Ice%252520and%252520Fire.jpg", - "https://lh5.googleusercontent.com/-7qZeDtRKFKc/URquWZT1gOI/AAAAAAAAAbs/hqWgteyNXsg/s1024/Another%252520Rockaway%252520Sunset.jpg", - "https://lh3.googleusercontent.com/--L0Km39l5J8/URquXHGcdNI/AAAAAAAAAbs/3ZrSJNrSomQ/s1024/Antelope%252520Butte.jpg", - "https://lh6.googleusercontent.com/-8HO-4vIFnlw/URquZnsFgtI/AAAAAAAAAbs/WT8jViTF7vw/s1024/Antelope%252520Hallway.jpg", - "https://lh4.googleusercontent.com/-WIuWgVcU3Qw/URqubRVcj4I/AAAAAAAAAbs/YvbwgGjwdIQ/s1024/Antelope%252520Walls.jpg", - "https://lh6.googleusercontent.com/-UBmLbPELvoQ/URqucCdv0kI/AAAAAAAAAbs/IdNhr2VQoQs/s1024/Apre%2525CC%252580s%252520la%252520Pluie.jpg", - "https://lh3.googleusercontent.com/-s-AFpvgSeew/URquc6dF-JI/AAAAAAAAAbs/Mt3xNGRUd68/s1024/Backlit%252520Cloud.jpg", - "https://lh5.googleusercontent.com/-bvmif9a9YOQ/URquea3heHI/AAAAAAAAAbs/rcr6wyeQtAo/s1024/Bee%252520and%252520Flower.jpg", - "https://lh5.googleusercontent.com/-n7mdm7I7FGs/URqueT_BT-I/AAAAAAAAAbs/9MYmXlmpSAo/s1024/Bonzai%252520Rock%252520Sunset.jpg", - "https://lh6.googleusercontent.com/-4CN4X4t0M1k/URqufPozWzI/AAAAAAAAAbs/8wK41lg1KPs/s1024/Caterpillar.jpg", - "https://lh3.googleusercontent.com/-rrFnVC8xQEg/URqufdrLBaI/AAAAAAAAAbs/s69WYy_fl1E/s1024/Chess.jpg", - "https://lh5.googleusercontent.com/-WVpRptWH8Yw/URqugh-QmDI/AAAAAAAAAbs/E-MgBgtlUWU/s1024/Chihuly.jpg", - "https://lh5.googleusercontent.com/-0BDXkYmckbo/URquhKFW84I/AAAAAAAAAbs/ogQtHCTk2JQ/s1024/Closed%252520Door.jpg", - "https://lh3.googleusercontent.com/-PyggXXZRykM/URquh-kVvoI/AAAAAAAAAbs/hFtDwhtrHHQ/s1024/Colorado%252520River%252520Sunset.jpg", - "https://lh3.googleusercontent.com/-ZAs4dNZtALc/URquikvOCWI/AAAAAAAAAbs/DXz4h3dll1Y/s1024/Colors%252520of%252520Autumn.jpg", - "https://lh4.googleusercontent.com/-GztnWEIiMz8/URqukVCU7bI/AAAAAAAAAbs/jo2Hjv6MZ6M/s1024/Countryside.jpg", - "https://lh4.googleusercontent.com/-bEg9EZ9QoiM/URquklz3FGI/AAAAAAAAAbs/UUuv8Ac2BaE/s1024/Death%252520Valley%252520-%252520Dunes.jpg", - "https://lh6.googleusercontent.com/-ijQJ8W68tEE/URqulGkvFEI/AAAAAAAAAbs/zPXvIwi_rFw/s1024/Delicate%252520Arch.jpg", - "https://lh5.googleusercontent.com/-Oh8mMy2ieng/URqullDwehI/AAAAAAAAAbs/TbdeEfsaIZY/s1024/Despair.jpg", - "https://lh5.googleusercontent.com/-gl0y4UiAOlk/URqumC_KjBI/AAAAAAAAAbs/PM1eT7dn4oo/s1024/Eagle%252520Fall%252520Sunrise.jpg", - "https://lh3.googleusercontent.com/-hYYHd2_vXPQ/URqumtJa9eI/AAAAAAAAAbs/wAalXVkbSh0/s1024/Electric%252520Storm.jpg", - "https://lh5.googleusercontent.com/-PyY_yiyjPTo/URqunUOhHFI/AAAAAAAAAbs/azZoULNuJXc/s1024/False%252520Kiva.jpg", - "https://lh6.googleusercontent.com/-PYvLVdvXywk/URqunwd8hfI/AAAAAAAAAbs/qiMwgkFvf6I/s1024/Fitzgerald%252520Streaks.jpg", - "https://lh4.googleusercontent.com/-KIR_UobIIqY/URquoCZ9SlI/AAAAAAAAAbs/Y4d4q8sXu4c/s1024/Foggy%252520Sunset.jpg", - "https://lh6.googleusercontent.com/-9lzOk_OWZH0/URquoo4xYoI/AAAAAAAAAbs/AwgzHtNVCwU/s1024/Frantic.jpg", - "https://lh3.googleusercontent.com/-0X3JNaKaz48/URqupH78wpI/AAAAAAAAAbs/lHXxu_zbH8s/s1024/Golden%252520Gate%252520Afternoon.jpg", - "https://lh6.googleusercontent.com/-95sb5ag7ABc/URqupl95RDI/AAAAAAAAAbs/g73R20iVTRA/s1024/Golden%252520Gate%252520Fog.jpg", - "https://lh3.googleusercontent.com/-JB9v6rtgHhk/URqup21F-zI/AAAAAAAAAbs/64Fb8qMZWXk/s1024/Golden%252520Grass.jpg", - "https://lh4.googleusercontent.com/-EIBGfnuLtII/URquqVHwaRI/AAAAAAAAAbs/FA4McV2u8VE/s1024/Grand%252520Teton.jpg", - "https://lh4.googleusercontent.com/-WoMxZvmN9nY/URquq1v2AoI/AAAAAAAAAbs/grj5uMhL6NA/s1024/Grass%252520Closeup.jpg", - "https://lh3.googleusercontent.com/-6hZiEHXx64Q/URqurxvNdqI/AAAAAAAAAbs/kWMXM3o5OVI/s1024/Green%252520Grass.jpg", - "https://lh5.googleusercontent.com/-6LVb9OXtQ60/URquteBFuKI/AAAAAAAAAbs/4F4kRgecwFs/s1024/Hanging%252520Leaf.jpg", - "https://lh4.googleusercontent.com/-zAvf__52ONk/URqutT_IuxI/AAAAAAAAAbs/D_bcuc0thoU/s1024/Highway%2525201.jpg", - "https://lh6.googleusercontent.com/-H4SrUg615rA/URquuL27fXI/AAAAAAAAAbs/4aEqJfiMsOU/s1024/Horseshoe%252520Bend%252520Sunset.jpg", - "https://lh4.googleusercontent.com/-JhFi4fb_Pqw/URquuX-QXbI/AAAAAAAAAbs/IXpYUxuweYM/s1024/Horseshoe%252520Bend.jpg", - "https://lh5.googleusercontent.com/-UGgssvFRJ7g/URquueyJzGI/AAAAAAAAAbs/yYIBlLT0toM/s1024/Into%252520the%252520Blue.jpg", - "https://lh3.googleusercontent.com/-CH7KoupI7uI/URquu0FF__I/AAAAAAAAAbs/R7GDmI7v_G0/s1024/Jelly%252520Fish%2525202.jpg", - "https://lh4.googleusercontent.com/-pwuuw6yhg8U/URquvPxR3FI/AAAAAAAAAbs/VNGk6f-tsGE/s1024/Jelly%252520Fish%2525203.jpg", - "https://lh5.googleusercontent.com/-GoUQVw1fnFw/URquv6xbC0I/AAAAAAAAAbs/zEUVTQQ43Zc/s1024/Kauai.jpg", - "https://lh6.googleusercontent.com/-8QdYYQEpYjw/URquwvdh88I/AAAAAAAAAbs/cktDy-ysfHo/s1024/Kyoto%252520Sunset.jpg", - "https://lh4.googleusercontent.com/-vPeekyDjOE0/URquwzJ28qI/AAAAAAAAAbs/qxcyXULsZrg/s1024/Lake%252520Tahoe%252520Colors.jpg", - "https://lh4.googleusercontent.com/-xBPxWpD4yxU/URquxWHk8AI/AAAAAAAAAbs/ARDPeDYPiMY/s1024/Lava%252520from%252520the%252520Sky.jpg", - "https://lh3.googleusercontent.com/-897VXrJB6RE/URquxxxd-5I/AAAAAAAAAbs/j-Cz4T4YvIw/s1024/Leica%25252050mm%252520Summilux.jpg", - "https://lh5.googleusercontent.com/-qSJ4D4iXzGo/URquyDWiJ1I/AAAAAAAAAbs/k2pBXeWehOA/s1024/Leica%25252050mm%252520Summilux.jpg", - "https://lh6.googleusercontent.com/-dwlPg83vzLg/URquylTVuFI/AAAAAAAAAbs/G6SyQ8b4YsI/s1024/Leica%252520M8%252520%252528Front%252529.jpg", - "https://lh3.googleusercontent.com/-R3_EYAyJvfk/URquzQBv8eI/AAAAAAAAAbs/b9xhpUM3pEI/s1024/Light%252520to%252520Sand.jpg", - "https://lh3.googleusercontent.com/-fHY5h67QPi0/URqu0Cp4J1I/AAAAAAAAAbs/0lG6m94Z6vM/s1024/Little%252520Bit%252520of%252520Paradise.jpg", - "https://lh5.googleusercontent.com/-TzF_LwrCnRM/URqu0RddPOI/AAAAAAAAAbs/gaj2dLiuX0s/s1024/Lone%252520Pine%252520Sunset.jpg", - "https://lh3.googleusercontent.com/-4HdpJ4_DXU4/URqu046dJ9I/AAAAAAAAAbs/eBOodtk2_uk/s1024/Lonely%252520Rock.jpg", - "https://lh6.googleusercontent.com/-erbF--z-W4s/URqu1ajSLkI/AAAAAAAAAbs/xjDCDO1INzM/s1024/Longue%252520Vue.jpg", - "https://lh6.googleusercontent.com/-0CXJRdJaqvc/URqu1opNZNI/AAAAAAAAAbs/PFB2oPUU7Lk/s1024/Look%252520Me%252520in%252520the%252520Eye.jpg", - "https://lh3.googleusercontent.com/-D_5lNxnDN6g/URqu2Tk7HVI/AAAAAAAAAbs/p0ddca9W__Y/s1024/Lost%252520in%252520a%252520Field.jpg", - "https://lh6.googleusercontent.com/-flsqwMrIk2Q/URqu24PcmjI/AAAAAAAAAbs/5ocIH85XofM/s1024/Marshall%252520Beach%252520Sunset.jpg", - "https://lh4.googleusercontent.com/-Y4lgryEVTmU/URqu28kG3gI/AAAAAAAAAbs/OjXpekqtbJ4/s1024/Mono%252520Lake%252520Blue.jpg", - "https://lh4.googleusercontent.com/-AaHAJPmcGYA/URqu3PIldHI/AAAAAAAAAbs/lcTqk1SIcRs/s1024/Monument%252520Valley%252520Overlook.jpg", - "https://lh4.googleusercontent.com/-vKxfdQ83dQA/URqu31Yq_BI/AAAAAAAAAbs/OUoGk_2AyfM/s1024/Moving%252520Rock.jpg", - "https://lh5.googleusercontent.com/-CG62QiPpWXg/URqu4ia4vRI/AAAAAAAAAbs/0YOdqLAlcAc/s1024/Napali%252520Coast.jpg", - "https://lh6.googleusercontent.com/-wdGrP5PMmJQ/URqu5PZvn7I/AAAAAAAAAbs/m0abEcdPXe4/s1024/One%252520Wheel.jpg", - "https://lh6.googleusercontent.com/-6WS5DoCGuOA/URqu5qx1UgI/AAAAAAAAAbs/giMw2ixPvrY/s1024/Open%252520Sky.jpg", - "https://lh6.googleusercontent.com/-u8EHKj8G8GQ/URqu55sM6yI/AAAAAAAAAbs/lIXX_GlTdmI/s1024/Orange%252520Sunset.jpg", - "https://lh6.googleusercontent.com/-74Z5qj4bTDE/URqu6LSrJrI/AAAAAAAAAbs/XzmVkw90szQ/s1024/Orchid.jpg", - "https://lh6.googleusercontent.com/-lEQE4h6TePE/URqu6t_lSkI/AAAAAAAAAbs/zvGYKOea_qY/s1024/Over%252520there.jpg", - "https://lh5.googleusercontent.com/-cauH-53JH2M/URqu66v_USI/AAAAAAAAAbs/EucwwqclfKQ/s1024/Plumes.jpg", - "https://lh3.googleusercontent.com/-eDLT2jHDoy4/URqu7axzkAI/AAAAAAAAAbs/iVZE-xJ7lZs/s1024/Rainbokeh.jpg", - "https://lh5.googleusercontent.com/-j1NLqEFIyco/URqu8L1CGcI/AAAAAAAAAbs/aqZkgX66zlI/s1024/Rainbow.jpg", - "https://lh5.googleusercontent.com/-DRnqmK0t4VU/URqu8XYN9yI/AAAAAAAAAbs/LgvF_592WLU/s1024/Rice%252520Fields.jpg", - "https://lh3.googleusercontent.com/-hwh1v3EOGcQ/URqu8qOaKwI/AAAAAAAAAbs/IljRJRnbJGw/s1024/Rockaway%252520Fire%252520Sky.jpg", - "https://lh5.googleusercontent.com/-wjV6FQk7tlk/URqu9jCQ8sI/AAAAAAAAAbs/RyYUpdo-c9o/s1024/Rockaway%252520Flow.jpg", - "https://lh6.googleusercontent.com/-6cAXNfo7D20/URqu-BdzgPI/AAAAAAAAAbs/OmsYllzJqwo/s1024/Rockaway%252520Sunset%252520Sky.jpg", - "https://lh3.googleusercontent.com/-sl8fpGPS-RE/URqu_BOkfgI/AAAAAAAAAbs/Dg2Fv-JxOeg/s1024/Russian%252520Ridge%252520Sunset.jpg", - "https://lh6.googleusercontent.com/-gVtY36mMBIg/URqu_q91lkI/AAAAAAAAAbs/3CiFMBcy5MA/s1024/Rust%252520Knot.jpg", - "https://lh6.googleusercontent.com/-GHeImuHqJBE/URqu_FKfVLI/AAAAAAAAAbs/axuEJeqam7Q/s1024/Sailing%252520Stones.jpg", - "https://lh3.googleusercontent.com/-hBbYZjTOwGc/URqu_ycpIrI/AAAAAAAAAbs/nAdJUXnGJYE/s1024/Seahorse.jpg", - "https://lh3.googleusercontent.com/-Iwi6-i6IexY/URqvAYZHsVI/AAAAAAAAAbs/5ETWl4qXsFE/s1024/Shinjuku%252520Street.jpg", - "https://lh6.googleusercontent.com/-amhnySTM_MY/URqvAlb5KoI/AAAAAAAAAbs/pFCFgzlKsn0/s1024/Sierra%252520Heavens.jpg", - "https://lh5.googleusercontent.com/-dJgjepFrYSo/URqvBVJZrAI/AAAAAAAAAbs/v-F5QWpYO6s/s1024/Sierra%252520Sunset.jpg", - "https://lh4.googleusercontent.com/-Z4zGiC5nWdc/URqvBdEwivI/AAAAAAAAAbs/ZRZR1VJ84QA/s1024/Sin%252520Lights.jpg", - "https://lh4.googleusercontent.com/-_0cYiWW8ccY/URqvBz3iM4I/AAAAAAAAAbs/9N_Wq8MhLTY/s1024/Starry%252520Lake.jpg", - "https://lh3.googleusercontent.com/-A9LMoRyuQUA/URqvCYx_JoI/AAAAAAAAAbs/s7sde1Bz9cI/s1024/Starry%252520Night.jpg", - "https://lh3.googleusercontent.com/-KtLJ3k858eY/URqvC_2h_bI/AAAAAAAAAbs/zzEBImwDA_g/s1024/Stream.jpg", - "https://lh5.googleusercontent.com/-dFB7Lad6RcA/URqvDUftwWI/AAAAAAAAAbs/BrhoUtXTN7o/s1024/Strip%252520Sunset.jpg", - "https://lh5.googleusercontent.com/-at6apgFiN20/URqvDyffUZI/AAAAAAAAAbs/clABCx171bE/s1024/Sunset%252520Hills.jpg", - "https://lh4.googleusercontent.com/-7-EHhtQthII/URqvEYTk4vI/AAAAAAAAAbs/QSJZoB3YjVg/s1024/Tenaya%252520Lake%2525202.jpg", - "https://lh6.googleusercontent.com/-8MrjV_a-Pok/URqvFC5repI/AAAAAAAAAbs/9inKTg9fbCE/s1024/Tenaya%252520Lake.jpg", - "https://lh5.googleusercontent.com/-B1HW-z4zwao/URqvFWYRwUI/AAAAAAAAAbs/8Peli53Bs8I/s1024/The%252520Cave%252520BW.jpg", - "https://lh3.googleusercontent.com/-PO4E-xZKAnQ/URqvGRqjYkI/AAAAAAAAAbs/42nyADFsXag/s1024/The%252520Fisherman.jpg", - "https://lh4.googleusercontent.com/-iLyZlzfdy7s/URqvG0YScdI/AAAAAAAAAbs/1J9eDKmkXtk/s1024/The%252520Night%252520is%252520Coming.jpg", - "https://lh6.googleusercontent.com/-G-k7YkkUco0/URqvHhah6fI/AAAAAAAAAbs/_taQQG7t0vo/s1024/The%252520Road.jpg", - "https://lh6.googleusercontent.com/-h-ALJt7kSus/URqvIThqYfI/AAAAAAAAAbs/ejiv35olWS8/s1024/Tokyo%252520Heights.jpg", - "https://lh5.googleusercontent.com/-Hy9k-TbS7xg/URqvIjQMOxI/AAAAAAAAAbs/RSpmmOATSkg/s1024/Tokyo%252520Highway.jpg", - "https://lh6.googleusercontent.com/-83oOvMb4OZs/URqvJL0T7lI/AAAAAAAAAbs/c5TECZ6RONM/s1024/Tokyo%252520Smog.jpg", - "https://lh3.googleusercontent.com/-FB-jfgREEfI/URqvJI3EXAI/AAAAAAAAAbs/XfyweiRF4v8/s1024/Tufa%252520at%252520Night.jpg", - "https://lh4.googleusercontent.com/-vngKD5Z1U8w/URqvJUCEgPI/AAAAAAAAAbs/ulxCMVcU6EU/s1024/Valley%252520Sunset.jpg", - "https://lh6.googleusercontent.com/-DOz5I2E2oMQ/URqvKMND1kI/AAAAAAAAAbs/Iqf0IsInleo/s1024/Windmill%252520Sunrise.jpg", - "https://lh5.googleusercontent.com/-biyiyWcJ9MU/URqvKculiAI/AAAAAAAAAbs/jyPsCplJOpE/s1024/Windmill.jpg", - "https://lh4.googleusercontent.com/-PDT167_xRdA/URqvK36mLcI/AAAAAAAAAbs/oi2ik9QseMI/s1024/Windmills.jpg", - "https://lh5.googleusercontent.com/-kI_QdYx7VlU/URqvLXCB6gI/AAAAAAAAAbs/N31vlZ6u89o/s1024/Yet%252520Another%252520Rockaway%252520Sunset.jpg", - "https://lh4.googleusercontent.com/-e9NHZ5k5MSs/URqvMIBZjtI/AAAAAAAAAbs/1fV810rDNfQ/s1024/Yosemite%252520Tree.jpg", - }; - - /** - * This are PicasaWeb thumbnail URLs and could potentially change. Ideally the PicasaWeb API - * should be used to fetch the URLs. - * - * Credit to Romain Guy for the photos: - * http://www.curious-creature.org/ - * https://plus.google.com/109538161516040592207/about - * http://www.flickr.com/photos/romainguy - */ - public final static String[] imageThumbUrls = new String[] { - "https://lh6.googleusercontent.com/-55osAWw3x0Q/URquUtcFr5I/AAAAAAAAAbs/rWlj1RUKrYI/s240-c/A%252520Photographer.jpg", - "https://lh4.googleusercontent.com/--dq8niRp7W4/URquVgmXvgI/AAAAAAAAAbs/-gnuLQfNnBA/s240-c/A%252520Song%252520of%252520Ice%252520and%252520Fire.jpg", - "https://lh5.googleusercontent.com/-7qZeDtRKFKc/URquWZT1gOI/AAAAAAAAAbs/hqWgteyNXsg/s240-c/Another%252520Rockaway%252520Sunset.jpg", - "https://lh3.googleusercontent.com/--L0Km39l5J8/URquXHGcdNI/AAAAAAAAAbs/3ZrSJNrSomQ/s240-c/Antelope%252520Butte.jpg", - "https://lh6.googleusercontent.com/-8HO-4vIFnlw/URquZnsFgtI/AAAAAAAAAbs/WT8jViTF7vw/s240-c/Antelope%252520Hallway.jpg", - "https://lh4.googleusercontent.com/-WIuWgVcU3Qw/URqubRVcj4I/AAAAAAAAAbs/YvbwgGjwdIQ/s240-c/Antelope%252520Walls.jpg", - "https://lh6.googleusercontent.com/-UBmLbPELvoQ/URqucCdv0kI/AAAAAAAAAbs/IdNhr2VQoQs/s240-c/Apre%2525CC%252580s%252520la%252520Pluie.jpg", - "https://lh3.googleusercontent.com/-s-AFpvgSeew/URquc6dF-JI/AAAAAAAAAbs/Mt3xNGRUd68/s240-c/Backlit%252520Cloud.jpg", - "https://lh5.googleusercontent.com/-bvmif9a9YOQ/URquea3heHI/AAAAAAAAAbs/rcr6wyeQtAo/s240-c/Bee%252520and%252520Flower.jpg", - "https://lh5.googleusercontent.com/-n7mdm7I7FGs/URqueT_BT-I/AAAAAAAAAbs/9MYmXlmpSAo/s240-c/Bonzai%252520Rock%252520Sunset.jpg", - "https://lh6.googleusercontent.com/-4CN4X4t0M1k/URqufPozWzI/AAAAAAAAAbs/8wK41lg1KPs/s240-c/Caterpillar.jpg", - "https://lh3.googleusercontent.com/-rrFnVC8xQEg/URqufdrLBaI/AAAAAAAAAbs/s69WYy_fl1E/s240-c/Chess.jpg", - "https://lh5.googleusercontent.com/-WVpRptWH8Yw/URqugh-QmDI/AAAAAAAAAbs/E-MgBgtlUWU/s240-c/Chihuly.jpg", - "https://lh5.googleusercontent.com/-0BDXkYmckbo/URquhKFW84I/AAAAAAAAAbs/ogQtHCTk2JQ/s240-c/Closed%252520Door.jpg", - "https://lh3.googleusercontent.com/-PyggXXZRykM/URquh-kVvoI/AAAAAAAAAbs/hFtDwhtrHHQ/s240-c/Colorado%252520River%252520Sunset.jpg", - "https://lh3.googleusercontent.com/-ZAs4dNZtALc/URquikvOCWI/AAAAAAAAAbs/DXz4h3dll1Y/s240-c/Colors%252520of%252520Autumn.jpg", - "https://lh4.googleusercontent.com/-GztnWEIiMz8/URqukVCU7bI/AAAAAAAAAbs/jo2Hjv6MZ6M/s240-c/Countryside.jpg", - "https://lh4.googleusercontent.com/-bEg9EZ9QoiM/URquklz3FGI/AAAAAAAAAbs/UUuv8Ac2BaE/s240-c/Death%252520Valley%252520-%252520Dunes.jpg", - "https://lh6.googleusercontent.com/-ijQJ8W68tEE/URqulGkvFEI/AAAAAAAAAbs/zPXvIwi_rFw/s240-c/Delicate%252520Arch.jpg", - "https://lh5.googleusercontent.com/-Oh8mMy2ieng/URqullDwehI/AAAAAAAAAbs/TbdeEfsaIZY/s240-c/Despair.jpg", - "https://lh5.googleusercontent.com/-gl0y4UiAOlk/URqumC_KjBI/AAAAAAAAAbs/PM1eT7dn4oo/s240-c/Eagle%252520Fall%252520Sunrise.jpg", - "https://lh3.googleusercontent.com/-hYYHd2_vXPQ/URqumtJa9eI/AAAAAAAAAbs/wAalXVkbSh0/s240-c/Electric%252520Storm.jpg", - "https://lh5.googleusercontent.com/-PyY_yiyjPTo/URqunUOhHFI/AAAAAAAAAbs/azZoULNuJXc/s240-c/False%252520Kiva.jpg", - "https://lh6.googleusercontent.com/-PYvLVdvXywk/URqunwd8hfI/AAAAAAAAAbs/qiMwgkFvf6I/s240-c/Fitzgerald%252520Streaks.jpg", - "https://lh4.googleusercontent.com/-KIR_UobIIqY/URquoCZ9SlI/AAAAAAAAAbs/Y4d4q8sXu4c/s240-c/Foggy%252520Sunset.jpg", - "https://lh6.googleusercontent.com/-9lzOk_OWZH0/URquoo4xYoI/AAAAAAAAAbs/AwgzHtNVCwU/s240-c/Frantic.jpg", - "https://lh3.googleusercontent.com/-0X3JNaKaz48/URqupH78wpI/AAAAAAAAAbs/lHXxu_zbH8s/s240-c/Golden%252520Gate%252520Afternoon.jpg", - "https://lh6.googleusercontent.com/-95sb5ag7ABc/URqupl95RDI/AAAAAAAAAbs/g73R20iVTRA/s240-c/Golden%252520Gate%252520Fog.jpg", - "https://lh3.googleusercontent.com/-JB9v6rtgHhk/URqup21F-zI/AAAAAAAAAbs/64Fb8qMZWXk/s240-c/Golden%252520Grass.jpg", - "https://lh4.googleusercontent.com/-EIBGfnuLtII/URquqVHwaRI/AAAAAAAAAbs/FA4McV2u8VE/s240-c/Grand%252520Teton.jpg", - "https://lh4.googleusercontent.com/-WoMxZvmN9nY/URquq1v2AoI/AAAAAAAAAbs/grj5uMhL6NA/s240-c/Grass%252520Closeup.jpg", - "https://lh3.googleusercontent.com/-6hZiEHXx64Q/URqurxvNdqI/AAAAAAAAAbs/kWMXM3o5OVI/s240-c/Green%252520Grass.jpg", - "https://lh5.googleusercontent.com/-6LVb9OXtQ60/URquteBFuKI/AAAAAAAAAbs/4F4kRgecwFs/s240-c/Hanging%252520Leaf.jpg", - "https://lh4.googleusercontent.com/-zAvf__52ONk/URqutT_IuxI/AAAAAAAAAbs/D_bcuc0thoU/s240-c/Highway%2525201.jpg", - "https://lh6.googleusercontent.com/-H4SrUg615rA/URquuL27fXI/AAAAAAAAAbs/4aEqJfiMsOU/s240-c/Horseshoe%252520Bend%252520Sunset.jpg", - "https://lh4.googleusercontent.com/-JhFi4fb_Pqw/URquuX-QXbI/AAAAAAAAAbs/IXpYUxuweYM/s240-c/Horseshoe%252520Bend.jpg", - "https://lh5.googleusercontent.com/-UGgssvFRJ7g/URquueyJzGI/AAAAAAAAAbs/yYIBlLT0toM/s240-c/Into%252520the%252520Blue.jpg", - "https://lh3.googleusercontent.com/-CH7KoupI7uI/URquu0FF__I/AAAAAAAAAbs/R7GDmI7v_G0/s240-c/Jelly%252520Fish%2525202.jpg", - "https://lh4.googleusercontent.com/-pwuuw6yhg8U/URquvPxR3FI/AAAAAAAAAbs/VNGk6f-tsGE/s240-c/Jelly%252520Fish%2525203.jpg", - "https://lh5.googleusercontent.com/-GoUQVw1fnFw/URquv6xbC0I/AAAAAAAAAbs/zEUVTQQ43Zc/s240-c/Kauai.jpg", - "https://lh6.googleusercontent.com/-8QdYYQEpYjw/URquwvdh88I/AAAAAAAAAbs/cktDy-ysfHo/s240-c/Kyoto%252520Sunset.jpg", - "https://lh4.googleusercontent.com/-vPeekyDjOE0/URquwzJ28qI/AAAAAAAAAbs/qxcyXULsZrg/s240-c/Lake%252520Tahoe%252520Colors.jpg", - "https://lh4.googleusercontent.com/-xBPxWpD4yxU/URquxWHk8AI/AAAAAAAAAbs/ARDPeDYPiMY/s240-c/Lava%252520from%252520the%252520Sky.jpg", - "https://lh3.googleusercontent.com/-897VXrJB6RE/URquxxxd-5I/AAAAAAAAAbs/j-Cz4T4YvIw/s240-c/Leica%25252050mm%252520Summilux.jpg", - "https://lh5.googleusercontent.com/-qSJ4D4iXzGo/URquyDWiJ1I/AAAAAAAAAbs/k2pBXeWehOA/s240-c/Leica%25252050mm%252520Summilux.jpg", - "https://lh6.googleusercontent.com/-dwlPg83vzLg/URquylTVuFI/AAAAAAAAAbs/G6SyQ8b4YsI/s240-c/Leica%252520M8%252520%252528Front%252529.jpg", - "https://lh3.googleusercontent.com/-R3_EYAyJvfk/URquzQBv8eI/AAAAAAAAAbs/b9xhpUM3pEI/s240-c/Light%252520to%252520Sand.jpg", - "https://lh3.googleusercontent.com/-fHY5h67QPi0/URqu0Cp4J1I/AAAAAAAAAbs/0lG6m94Z6vM/s240-c/Little%252520Bit%252520of%252520Paradise.jpg", - "https://lh5.googleusercontent.com/-TzF_LwrCnRM/URqu0RddPOI/AAAAAAAAAbs/gaj2dLiuX0s/s240-c/Lone%252520Pine%252520Sunset.jpg", - "https://lh3.googleusercontent.com/-4HdpJ4_DXU4/URqu046dJ9I/AAAAAAAAAbs/eBOodtk2_uk/s240-c/Lonely%252520Rock.jpg", - "https://lh6.googleusercontent.com/-erbF--z-W4s/URqu1ajSLkI/AAAAAAAAAbs/xjDCDO1INzM/s240-c/Longue%252520Vue.jpg", - "https://lh6.googleusercontent.com/-0CXJRdJaqvc/URqu1opNZNI/AAAAAAAAAbs/PFB2oPUU7Lk/s240-c/Look%252520Me%252520in%252520the%252520Eye.jpg", - "https://lh3.googleusercontent.com/-D_5lNxnDN6g/URqu2Tk7HVI/AAAAAAAAAbs/p0ddca9W__Y/s240-c/Lost%252520in%252520a%252520Field.jpg", - "https://lh6.googleusercontent.com/-flsqwMrIk2Q/URqu24PcmjI/AAAAAAAAAbs/5ocIH85XofM/s240-c/Marshall%252520Beach%252520Sunset.jpg", - "https://lh4.googleusercontent.com/-Y4lgryEVTmU/URqu28kG3gI/AAAAAAAAAbs/OjXpekqtbJ4/s240-c/Mono%252520Lake%252520Blue.jpg", - "https://lh4.googleusercontent.com/-AaHAJPmcGYA/URqu3PIldHI/AAAAAAAAAbs/lcTqk1SIcRs/s240-c/Monument%252520Valley%252520Overlook.jpg", - "https://lh4.googleusercontent.com/-vKxfdQ83dQA/URqu31Yq_BI/AAAAAAAAAbs/OUoGk_2AyfM/s240-c/Moving%252520Rock.jpg", - "https://lh5.googleusercontent.com/-CG62QiPpWXg/URqu4ia4vRI/AAAAAAAAAbs/0YOdqLAlcAc/s240-c/Napali%252520Coast.jpg", - "https://lh6.googleusercontent.com/-wdGrP5PMmJQ/URqu5PZvn7I/AAAAAAAAAbs/m0abEcdPXe4/s240-c/One%252520Wheel.jpg", - "https://lh6.googleusercontent.com/-6WS5DoCGuOA/URqu5qx1UgI/AAAAAAAAAbs/giMw2ixPvrY/s240-c/Open%252520Sky.jpg", - "https://lh6.googleusercontent.com/-u8EHKj8G8GQ/URqu55sM6yI/AAAAAAAAAbs/lIXX_GlTdmI/s240-c/Orange%252520Sunset.jpg", - "https://lh6.googleusercontent.com/-74Z5qj4bTDE/URqu6LSrJrI/AAAAAAAAAbs/XzmVkw90szQ/s240-c/Orchid.jpg", - "https://lh6.googleusercontent.com/-lEQE4h6TePE/URqu6t_lSkI/AAAAAAAAAbs/zvGYKOea_qY/s240-c/Over%252520there.jpg", - "https://lh5.googleusercontent.com/-cauH-53JH2M/URqu66v_USI/AAAAAAAAAbs/EucwwqclfKQ/s240-c/Plumes.jpg", - "https://lh3.googleusercontent.com/-eDLT2jHDoy4/URqu7axzkAI/AAAAAAAAAbs/iVZE-xJ7lZs/s240-c/Rainbokeh.jpg", - "https://lh5.googleusercontent.com/-j1NLqEFIyco/URqu8L1CGcI/AAAAAAAAAbs/aqZkgX66zlI/s240-c/Rainbow.jpg", - "https://lh5.googleusercontent.com/-DRnqmK0t4VU/URqu8XYN9yI/AAAAAAAAAbs/LgvF_592WLU/s240-c/Rice%252520Fields.jpg", - "https://lh3.googleusercontent.com/-hwh1v3EOGcQ/URqu8qOaKwI/AAAAAAAAAbs/IljRJRnbJGw/s240-c/Rockaway%252520Fire%252520Sky.jpg", - "https://lh5.googleusercontent.com/-wjV6FQk7tlk/URqu9jCQ8sI/AAAAAAAAAbs/RyYUpdo-c9o/s240-c/Rockaway%252520Flow.jpg", - "https://lh6.googleusercontent.com/-6cAXNfo7D20/URqu-BdzgPI/AAAAAAAAAbs/OmsYllzJqwo/s240-c/Rockaway%252520Sunset%252520Sky.jpg", - "https://lh3.googleusercontent.com/-sl8fpGPS-RE/URqu_BOkfgI/AAAAAAAAAbs/Dg2Fv-JxOeg/s240-c/Russian%252520Ridge%252520Sunset.jpg", - "https://lh6.googleusercontent.com/-gVtY36mMBIg/URqu_q91lkI/AAAAAAAAAbs/3CiFMBcy5MA/s240-c/Rust%252520Knot.jpg", - "https://lh6.googleusercontent.com/-GHeImuHqJBE/URqu_FKfVLI/AAAAAAAAAbs/axuEJeqam7Q/s240-c/Sailing%252520Stones.jpg", - "https://lh3.googleusercontent.com/-hBbYZjTOwGc/URqu_ycpIrI/AAAAAAAAAbs/nAdJUXnGJYE/s240-c/Seahorse.jpg", - "https://lh3.googleusercontent.com/-Iwi6-i6IexY/URqvAYZHsVI/AAAAAAAAAbs/5ETWl4qXsFE/s240-c/Shinjuku%252520Street.jpg", - "https://lh6.googleusercontent.com/-amhnySTM_MY/URqvAlb5KoI/AAAAAAAAAbs/pFCFgzlKsn0/s240-c/Sierra%252520Heavens.jpg", - "https://lh5.googleusercontent.com/-dJgjepFrYSo/URqvBVJZrAI/AAAAAAAAAbs/v-F5QWpYO6s/s240-c/Sierra%252520Sunset.jpg", - "https://lh4.googleusercontent.com/-Z4zGiC5nWdc/URqvBdEwivI/AAAAAAAAAbs/ZRZR1VJ84QA/s240-c/Sin%252520Lights.jpg", - "https://lh4.googleusercontent.com/-_0cYiWW8ccY/URqvBz3iM4I/AAAAAAAAAbs/9N_Wq8MhLTY/s240-c/Starry%252520Lake.jpg", - "https://lh3.googleusercontent.com/-A9LMoRyuQUA/URqvCYx_JoI/AAAAAAAAAbs/s7sde1Bz9cI/s240-c/Starry%252520Night.jpg", - "https://lh3.googleusercontent.com/-KtLJ3k858eY/URqvC_2h_bI/AAAAAAAAAbs/zzEBImwDA_g/s240-c/Stream.jpg", - "https://lh5.googleusercontent.com/-dFB7Lad6RcA/URqvDUftwWI/AAAAAAAAAbs/BrhoUtXTN7o/s240-c/Strip%252520Sunset.jpg", - "https://lh5.googleusercontent.com/-at6apgFiN20/URqvDyffUZI/AAAAAAAAAbs/clABCx171bE/s240-c/Sunset%252520Hills.jpg", - "https://lh4.googleusercontent.com/-7-EHhtQthII/URqvEYTk4vI/AAAAAAAAAbs/QSJZoB3YjVg/s240-c/Tenaya%252520Lake%2525202.jpg", - "https://lh6.googleusercontent.com/-8MrjV_a-Pok/URqvFC5repI/AAAAAAAAAbs/9inKTg9fbCE/s240-c/Tenaya%252520Lake.jpg", - "https://lh5.googleusercontent.com/-B1HW-z4zwao/URqvFWYRwUI/AAAAAAAAAbs/8Peli53Bs8I/s240-c/The%252520Cave%252520BW.jpg", - "https://lh3.googleusercontent.com/-PO4E-xZKAnQ/URqvGRqjYkI/AAAAAAAAAbs/42nyADFsXag/s240-c/The%252520Fisherman.jpg", - "https://lh4.googleusercontent.com/-iLyZlzfdy7s/URqvG0YScdI/AAAAAAAAAbs/1J9eDKmkXtk/s240-c/The%252520Night%252520is%252520Coming.jpg", - "https://lh6.googleusercontent.com/-G-k7YkkUco0/URqvHhah6fI/AAAAAAAAAbs/_taQQG7t0vo/s240-c/The%252520Road.jpg", - "https://lh6.googleusercontent.com/-h-ALJt7kSus/URqvIThqYfI/AAAAAAAAAbs/ejiv35olWS8/s240-c/Tokyo%252520Heights.jpg", - "https://lh5.googleusercontent.com/-Hy9k-TbS7xg/URqvIjQMOxI/AAAAAAAAAbs/RSpmmOATSkg/s240-c/Tokyo%252520Highway.jpg", - "https://lh6.googleusercontent.com/-83oOvMb4OZs/URqvJL0T7lI/AAAAAAAAAbs/c5TECZ6RONM/s240-c/Tokyo%252520Smog.jpg", - "https://lh3.googleusercontent.com/-FB-jfgREEfI/URqvJI3EXAI/AAAAAAAAAbs/XfyweiRF4v8/s240-c/Tufa%252520at%252520Night.jpg", - "https://lh4.googleusercontent.com/-vngKD5Z1U8w/URqvJUCEgPI/AAAAAAAAAbs/ulxCMVcU6EU/s240-c/Valley%252520Sunset.jpg", - "https://lh6.googleusercontent.com/-DOz5I2E2oMQ/URqvKMND1kI/AAAAAAAAAbs/Iqf0IsInleo/s240-c/Windmill%252520Sunrise.jpg", - "https://lh5.googleusercontent.com/-biyiyWcJ9MU/URqvKculiAI/AAAAAAAAAbs/jyPsCplJOpE/s240-c/Windmill.jpg", - "https://lh4.googleusercontent.com/-PDT167_xRdA/URqvK36mLcI/AAAAAAAAAbs/oi2ik9QseMI/s240-c/Windmills.jpg", - "https://lh5.googleusercontent.com/-kI_QdYx7VlU/URqvLXCB6gI/AAAAAAAAAbs/N31vlZ6u89o/s240-c/Yet%252520Another%252520Rockaway%252520Sunset.jpg", - "https://lh4.googleusercontent.com/-e9NHZ5k5MSs/URqvMIBZjtI/AAAAAAAAAbs/1fV810rDNfQ/s240-c/Yosemite%252520Tree.jpg", - }; -} diff --git a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/ui/ImageDetailActivity.java b/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/ui/ImageDetailActivity.java deleted file mode 100644 index 05d7b03f0..000000000 --- a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/ui/ImageDetailActivity.java +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Copyright (C) 2012 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.example.android.bitmapfun.ui; - -import android.annotation.TargetApi; -import android.app.ActionBar; -import android.os.Build.VERSION_CODES; -import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentActivity; -import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentStatePagerAdapter; -import android.support.v4.app.NavUtils; -import android.support.v4.view.ViewPager; -import android.util.DisplayMetrics; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.WindowManager.LayoutParams; -import android.widget.Toast; - -import com.example.android.bitmapfun.BuildConfig; -import com.example.android.bitmapfun.R; -import com.example.android.bitmapfun.provider.Images; -import com.example.android.bitmapfun.util.ImageCache; -import com.example.android.bitmapfun.util.ImageFetcher; -import com.example.android.bitmapfun.util.Utils; - -public class ImageDetailActivity extends FragmentActivity implements OnClickListener { - private static final String IMAGE_CACHE_DIR = "images"; - public static final String EXTRA_IMAGE = "extra_image"; - - private ImagePagerAdapter mAdapter; - private ImageFetcher mImageFetcher; - private ViewPager mPager; - - @TargetApi(VERSION_CODES.HONEYCOMB) - @Override - public void onCreate(Bundle savedInstanceState) { - if (BuildConfig.DEBUG) { - Utils.enableStrictMode(); - } - super.onCreate(savedInstanceState); - setContentView(R.layout.image_detail_pager); - - // Fetch screen height and width, to use as our max size when loading images as this - // activity runs full screen - final DisplayMetrics displayMetrics = new DisplayMetrics(); - getWindowManager().getDefaultDisplay().getMetrics(displayMetrics); - final int height = displayMetrics.heightPixels; - final int width = displayMetrics.widthPixels; - - // For this sample we'll use half of the longest width to resize our images. As the - // image scaling ensures the image is larger than this, we should be left with a - // resolution that is appropriate for both portrait and landscape. For best image quality - // we shouldn't divide by 2, but this will use more memory and require a larger memory - // cache. - final int longest = (height > width ? height : width) / 2; - - ImageCache.ImageCacheParams cacheParams = - new ImageCache.ImageCacheParams(this, IMAGE_CACHE_DIR); - cacheParams.setMemCacheSizePercent(0.25f); // Set memory cache to 25% of app memory - - // The ImageFetcher takes care of loading images into our ImageView children asynchronously - mImageFetcher = new ImageFetcher(this, longest); - mImageFetcher.addImageCache(getSupportFragmentManager(), cacheParams); - mImageFetcher.setImageFadeIn(false); - - // Set up ViewPager and backing adapter - mAdapter = new ImagePagerAdapter(getSupportFragmentManager(), Images.imageUrls.length); - mPager = (ViewPager) findViewById(R.id.pager); - mPager.setAdapter(mAdapter); - mPager.setPageMargin((int) getResources().getDimension(R.dimen.image_detail_pager_margin)); - mPager.setOffscreenPageLimit(2); - - // Set up activity to go full screen - getWindow().addFlags(LayoutParams.FLAG_FULLSCREEN); - - // Enable some additional newer visibility and ActionBar features to create a more - // immersive photo viewing experience - if (Utils.hasHoneycomb()) { - final ActionBar actionBar = getActionBar(); - - // Hide title text and set home as up - actionBar.setDisplayShowTitleEnabled(false); - actionBar.setDisplayHomeAsUpEnabled(true); - - // Hide and show the ActionBar as the visibility changes - mPager.setOnSystemUiVisibilityChangeListener( - new View.OnSystemUiVisibilityChangeListener() { - @Override - public void onSystemUiVisibilityChange(int vis) { - if ((vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0) { - actionBar.hide(); - } else { - actionBar.show(); - } - } - }); - - // Start low profile mode and hide ActionBar - mPager.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE); - actionBar.hide(); - } - - // Set the current item based on the extra passed in to this activity - final int extraCurrentItem = getIntent().getIntExtra(EXTRA_IMAGE, -1); - if (extraCurrentItem != -1) { - mPager.setCurrentItem(extraCurrentItem); - } - } - - @Override - public void onResume() { - super.onResume(); - mImageFetcher.setExitTasksEarly(false); - } - - @Override - protected void onPause() { - super.onPause(); - mImageFetcher.setExitTasksEarly(true); - mImageFetcher.flushCache(); - } - - @Override - protected void onDestroy() { - super.onDestroy(); - mImageFetcher.closeCache(); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case android.R.id.home: - NavUtils.navigateUpFromSameTask(this); - return true; - case R.id.clear_cache: - mImageFetcher.clearCache(); - Toast.makeText( - this, R.string.clear_cache_complete_toast,Toast.LENGTH_SHORT).show(); - return true; - } - return super.onOptionsItemSelected(item); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.main_menu, menu); - return true; - } - - /** - * Called by the ViewPager child fragments to load images via the one ImageFetcher - */ - public ImageFetcher getImageFetcher() { - return mImageFetcher; - } - - /** - * The main adapter that backs the ViewPager. A subclass of FragmentStatePagerAdapter as there - * could be a large number of items in the ViewPager and we don't want to retain them all in - * memory at once but create/destroy them on the fly. - */ - private class ImagePagerAdapter extends FragmentStatePagerAdapter { - private final int mSize; - - public ImagePagerAdapter(FragmentManager fm, int size) { - super(fm); - mSize = size; - } - - @Override - public int getCount() { - return mSize; - } - - @Override - public Fragment getItem(int position) { - return ImageDetailFragment.newInstance(Images.imageUrls[position]); - } - } - - /** - * Set on the ImageView in the ViewPager children fragments, to enable/disable low profile mode - * when the ImageView is touched. - */ - @TargetApi(VERSION_CODES.HONEYCOMB) - @Override - public void onClick(View v) { - final int vis = mPager.getSystemUiVisibility(); - if ((vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0) { - mPager.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE); - } else { - mPager.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE); - } - } -} diff --git a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/ui/ImageDetailFragment.java b/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/ui/ImageDetailFragment.java deleted file mode 100644 index 9fff8a08a..000000000 --- a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/ui/ImageDetailFragment.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (C) 2012 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.example.android.bitmapfun.ui; - -import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.view.LayoutInflater; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; -import android.widget.ImageView; - -import com.example.android.bitmapfun.R; -import com.example.android.bitmapfun.util.ImageFetcher; -import com.example.android.bitmapfun.util.ImageWorker; -import com.example.android.bitmapfun.util.Utils; - -/** - * This fragment will populate the children of the ViewPager from {@link ImageDetailActivity}. - */ -public class ImageDetailFragment extends Fragment { - private static final String IMAGE_DATA_EXTRA = "extra_image_data"; - private String mImageUrl; - private ImageView mImageView; - private ImageFetcher mImageFetcher; - - /** - * Factory method to generate a new instance of the fragment given an image number. - * - * @param imageUrl The image url to load - * @return A new instance of ImageDetailFragment with imageNum extras - */ - public static ImageDetailFragment newInstance(String imageUrl) { - final ImageDetailFragment f = new ImageDetailFragment(); - - final Bundle args = new Bundle(); - args.putString(IMAGE_DATA_EXTRA, imageUrl); - f.setArguments(args); - - return f; - } - - /** - * Empty constructor as per the Fragment documentation - */ - public ImageDetailFragment() {} - - /** - * Populate image using a url from extras, use the convenience factory method - * {@link ImageDetailFragment#newInstance(String)} to create this fragment. - */ - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - mImageUrl = getArguments() != null ? getArguments().getString(IMAGE_DATA_EXTRA) : null; - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - // Inflate and locate the main ImageView - final View v = inflater.inflate(R.layout.image_detail_fragment, container, false); - mImageView = (ImageView) v.findViewById(R.id.imageView); - return v; - } - - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - // Use the parent activity to load the image asynchronously into the ImageView (so a single - // cache can be used over all pages in the ViewPager - if (ImageDetailActivity.class.isInstance(getActivity())) { - mImageFetcher = ((ImageDetailActivity) getActivity()).getImageFetcher(); - mImageFetcher.loadImage(mImageUrl, mImageView); - } - - // Pass clicks on the ImageView to the parent activity to handle - if (OnClickListener.class.isInstance(getActivity()) && Utils.hasHoneycomb()) { - mImageView.setOnClickListener((OnClickListener) getActivity()); - } - } - - @Override - public void onDestroy() { - super.onDestroy(); - if (mImageView != null) { - // Cancel any pending image work - ImageWorker.cancelWork(mImageView); - mImageView.setImageDrawable(null); - } - } -} diff --git a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/ui/ImageGridActivity.java b/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/ui/ImageGridActivity.java deleted file mode 100644 index e7c7d1c43..000000000 --- a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/ui/ImageGridActivity.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2012 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.example.android.bitmapfun.ui; - -import android.os.Bundle; -import android.support.v4.app.FragmentActivity; -import android.support.v4.app.FragmentTransaction; - -import com.example.android.bitmapfun.BuildConfig; -import com.example.android.bitmapfun.util.Utils; - -/** - * Simple FragmentActivity to hold the main {@link ImageGridFragment} and not much else. - */ -public class ImageGridActivity extends FragmentActivity { - private static final String TAG = "ImageGridActivity"; - - @Override - protected void onCreate(Bundle savedInstanceState) { - if (BuildConfig.DEBUG) { - Utils.enableStrictMode(); - } - super.onCreate(savedInstanceState); - - if (getSupportFragmentManager().findFragmentByTag(TAG) == null) { - final FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); - ft.add(android.R.id.content, new ImageGridFragment(), TAG); - ft.commit(); - } - } -} diff --git a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/ui/ImageGridFragment.java b/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/ui/ImageGridFragment.java deleted file mode 100644 index ba4581aa1..000000000 --- a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/ui/ImageGridFragment.java +++ /dev/null @@ -1,332 +0,0 @@ -/* - * Copyright (C) 2012 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.example.android.bitmapfun.ui; - -import android.annotation.TargetApi; -import android.app.ActivityOptions; -import android.content.Context; -import android.content.Intent; -import android.os.Build.VERSION_CODES; -import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.util.Log; -import android.util.TypedValue; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.view.ViewGroup.LayoutParams; -import android.view.ViewTreeObserver; -import android.widget.AbsListView; -import android.widget.AdapterView; -import android.widget.BaseAdapter; -import android.widget.GridView; -import android.widget.ImageView; -import android.widget.Toast; - -import com.example.android.bitmapfun.BuildConfig; -import com.example.android.bitmapfun.R; -import com.example.android.bitmapfun.provider.Images; -import com.example.android.bitmapfun.util.ImageCache.ImageCacheParams; -import com.example.android.bitmapfun.util.ImageFetcher; -import com.example.android.bitmapfun.util.Utils; - -/** - * The main fragment that powers the ImageGridActivity screen. Fairly straight forward GridView - * implementation with the key addition being the ImageWorker class w/ImageCache to load children - * asynchronously, keeping the UI nice and smooth and caching thumbnails for quick retrieval. The - * cache is retained over configuration changes like orientation change so the images are populated - * quickly if, for example, the user rotates the device. - */ -public class ImageGridFragment extends Fragment implements AdapterView.OnItemClickListener { - private static final String TAG = "ImageGridFragment"; - private static final String IMAGE_CACHE_DIR = "thumbs"; - - private int mImageThumbSize; - private int mImageThumbSpacing; - private ImageAdapter mAdapter; - private ImageFetcher mImageFetcher; - - /** - * Empty constructor as per the Fragment documentation - */ - public ImageGridFragment() {} - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setHasOptionsMenu(true); - - mImageThumbSize = getResources().getDimensionPixelSize(R.dimen.image_thumbnail_size); - mImageThumbSpacing = getResources().getDimensionPixelSize(R.dimen.image_thumbnail_spacing); - - mAdapter = new ImageAdapter(getActivity()); - - ImageCacheParams cacheParams = new ImageCacheParams(getActivity(), IMAGE_CACHE_DIR); - - cacheParams.setMemCacheSizePercent(0.25f); // Set memory cache to 25% of app memory - - // The ImageFetcher takes care of loading images into our ImageView children asynchronously - mImageFetcher = new ImageFetcher(getActivity(), mImageThumbSize); - mImageFetcher.setLoadingImage(R.drawable.empty_photo); - mImageFetcher.addImageCache(getActivity().getSupportFragmentManager(), cacheParams); - } - - @Override - public View onCreateView( - LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - - final View v = inflater.inflate(R.layout.image_grid_fragment, container, false); - final GridView mGridView = (GridView) v.findViewById(R.id.gridView); - mGridView.setAdapter(mAdapter); - mGridView.setOnItemClickListener(this); - mGridView.setOnScrollListener(new AbsListView.OnScrollListener() { - @Override - public void onScrollStateChanged(AbsListView absListView, int scrollState) { - // Pause fetcher to ensure smoother scrolling when flinging - if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_FLING) { - // Before Honeycomb pause image loading on scroll to help with performance - if (!Utils.hasHoneycomb()) { - mImageFetcher.setPauseWork(true); - } - } else { - mImageFetcher.setPauseWork(false); - } - } - - @Override - public void onScroll(AbsListView absListView, int firstVisibleItem, - int visibleItemCount, int totalItemCount) { - } - }); - - // This listener is used to get the final width of the GridView and then calculate the - // number of columns and the width of each column. The width of each column is variable - // as the GridView has stretchMode=columnWidth. The column width is used to set the height - // of each view so we get nice square thumbnails. - mGridView.getViewTreeObserver().addOnGlobalLayoutListener( - new ViewTreeObserver.OnGlobalLayoutListener() { - @Override - public void onGlobalLayout() { - if (mAdapter.getNumColumns() == 0) { - final int numColumns = (int) Math.floor( - mGridView.getWidth() / (mImageThumbSize + mImageThumbSpacing)); - if (numColumns > 0) { - final int columnWidth = - (mGridView.getWidth() / numColumns) - mImageThumbSpacing; - mAdapter.setNumColumns(numColumns); - mAdapter.setItemHeight(columnWidth); - if (BuildConfig.DEBUG) { - Log.d(TAG, "onCreateView - numColumns set to " + numColumns); - } - if (Utils.hasJellyBean()) { - mGridView.getViewTreeObserver() - .removeOnGlobalLayoutListener(this); - } else { - mGridView.getViewTreeObserver() - .removeGlobalOnLayoutListener(this); - } - } - } - } - }); - - return v; - } - - @Override - public void onResume() { - super.onResume(); - mImageFetcher.setExitTasksEarly(false); - mAdapter.notifyDataSetChanged(); - } - - @Override - public void onPause() { - super.onPause(); - mImageFetcher.setPauseWork(false); - mImageFetcher.setExitTasksEarly(true); - mImageFetcher.flushCache(); - } - - @Override - public void onDestroy() { - super.onDestroy(); - mImageFetcher.closeCache(); - } - - @TargetApi(VERSION_CODES.JELLY_BEAN) - @Override - public void onItemClick(AdapterView parent, View v, int position, long id) { - final Intent i = new Intent(getActivity(), ImageDetailActivity.class); - i.putExtra(ImageDetailActivity.EXTRA_IMAGE, (int) id); - if (Utils.hasJellyBean()) { - // makeThumbnailScaleUpAnimation() looks kind of ugly here as the loading spinner may - // show plus the thumbnail image in GridView is cropped. so using - // makeScaleUpAnimation() instead. - ActivityOptions options = - ActivityOptions.makeScaleUpAnimation(v, 0, 0, v.getWidth(), v.getHeight()); - getActivity().startActivity(i, options.toBundle()); - } else { - startActivity(i); - } - } - - @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - inflater.inflate(R.menu.main_menu, menu); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.clear_cache: - mImageFetcher.clearCache(); - Toast.makeText(getActivity(), R.string.clear_cache_complete_toast, - Toast.LENGTH_SHORT).show(); - return true; - } - return super.onOptionsItemSelected(item); - } - - /** - * The main adapter that backs the GridView. This is fairly standard except the number of - * columns in the GridView is used to create a fake top row of empty views as we use a - * transparent ActionBar and don't want the real top row of images to start off covered by it. - */ - private class ImageAdapter extends BaseAdapter { - - private final Context mContext; - private int mItemHeight = 0; - private int mNumColumns = 0; - private int mActionBarHeight = 0; - private GridView.LayoutParams mImageViewLayoutParams; - - public ImageAdapter(Context context) { - super(); - mContext = context; - mImageViewLayoutParams = new GridView.LayoutParams( - LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); - // Calculate ActionBar height - TypedValue tv = new TypedValue(); - if (context.getTheme().resolveAttribute( - android.R.attr.actionBarSize, tv, true)) { - mActionBarHeight = TypedValue.complexToDimensionPixelSize( - tv.data, context.getResources().getDisplayMetrics()); - } - } - - @Override - public int getCount() { - // If columns have yet to be determined, return no items - if (getNumColumns() == 0) { - return 0; - } - - // Size + number of columns for top empty row - return Images.imageThumbUrls.length + mNumColumns; - } - - @Override - public Object getItem(int position) { - return position < mNumColumns ? - null : Images.imageThumbUrls[position - mNumColumns]; - } - - @Override - public long getItemId(int position) { - return position < mNumColumns ? 0 : position - mNumColumns; - } - - @Override - public int getViewTypeCount() { - // Two types of views, the normal ImageView and the top row of empty views - return 2; - } - - @Override - public int getItemViewType(int position) { - return (position < mNumColumns) ? 1 : 0; - } - - @Override - public boolean hasStableIds() { - return true; - } - - @Override - public View getView(int position, View convertView, ViewGroup container) { - // First check if this is the top row - if (position < mNumColumns) { - if (convertView == null) { - convertView = new View(mContext); - } - // Set empty view with height of ActionBar - convertView.setLayoutParams(new AbsListView.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, mActionBarHeight)); - return convertView; - } - - // Now handle the main ImageView thumbnails - ImageView imageView; - if (convertView == null) { // if it's not recycled, instantiate and initialize - imageView = new RecyclingImageView(mContext); - imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); - imageView.setLayoutParams(mImageViewLayoutParams); - } else { // Otherwise re-use the converted view - imageView = (ImageView) convertView; - } - - // Check the height matches our calculated column width - if (imageView.getLayoutParams().height != mItemHeight) { - imageView.setLayoutParams(mImageViewLayoutParams); - } - - // Finally load the image asynchronously into the ImageView, this also takes care of - // setting a placeholder image while the background thread runs - mImageFetcher.loadImage(Images.imageThumbUrls[position - mNumColumns], imageView); - return imageView; - } - - /** - * Sets the item height. Useful for when we know the column width so the height can be set - * to match. - * - * @param height - */ - public void setItemHeight(int height) { - if (height == mItemHeight) { - return; - } - mItemHeight = height; - mImageViewLayoutParams = - new GridView.LayoutParams(LayoutParams.MATCH_PARENT, mItemHeight); - mImageFetcher.setImageSize(height); - notifyDataSetChanged(); - } - - public void setNumColumns(int numColumns) { - mNumColumns = numColumns; - } - - public int getNumColumns() { - return mNumColumns; - } - } -} diff --git a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/ui/RecyclingImageView.java b/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/ui/RecyclingImageView.java deleted file mode 100644 index 1bcc01465..000000000 --- a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/ui/RecyclingImageView.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (C) 2013 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.example.android.bitmapfun.ui; - -import android.content.Context; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.LayerDrawable; -import android.util.AttributeSet; -import android.widget.ImageView; - -import com.example.android.bitmapfun.util.RecyclingBitmapDrawable; - -/** - * Sub-class of ImageView which automatically notifies the drawable when it is - * being displayed. - */ -public class RecyclingImageView extends ImageView { - - public RecyclingImageView(Context context) { - super(context); - } - - public RecyclingImageView(Context context, AttributeSet attrs) { - super(context, attrs); - } - - /** - * @see android.widget.ImageView#onDetachedFromWindow() - */ - @Override - protected void onDetachedFromWindow() { - // This has been detached from Window, so clear the drawable - setImageDrawable(null); - - super.onDetachedFromWindow(); - } - - /** - * @see android.widget.ImageView#setImageDrawable(android.graphics.drawable.Drawable) - */ - @Override - public void setImageDrawable(Drawable drawable) { - // Keep hold of previous Drawable - final Drawable previousDrawable = getDrawable(); - - // Call super to set new Drawable - super.setImageDrawable(drawable); - - // Notify new Drawable that it is being displayed - notifyDrawable(drawable, true); - - // Notify old Drawable so it is no longer being displayed - notifyDrawable(previousDrawable, false); - } - - /** - * Notifies the drawable that it's displayed state has changed. - * - * @param drawable - * @param isDisplayed - */ - private static void notifyDrawable(Drawable drawable, final boolean isDisplayed) { - if (drawable instanceof RecyclingBitmapDrawable) { - // The drawable is a CountingBitmapDrawable, so notify it - ((RecyclingBitmapDrawable) drawable).setIsDisplayed(isDisplayed); - } else if (drawable instanceof LayerDrawable) { - // The drawable is a LayerDrawable, so recurse on each layer - LayerDrawable layerDrawable = (LayerDrawable) drawable; - for (int i = 0, z = layerDrawable.getNumberOfLayers(); i < z; i++) { - notifyDrawable(layerDrawable.getDrawable(i), isDisplayed); - } - } - } - -} diff --git a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/AsyncTask.java b/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/AsyncTask.java deleted file mode 100644 index 018ce1a23..000000000 --- a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/AsyncTask.java +++ /dev/null @@ -1,693 +0,0 @@ -/* - * Copyright (C) 2008 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.example.android.bitmapfun.util; - -import android.annotation.TargetApi; -import android.os.Handler; -import android.os.Message; -import android.os.Process; - -import java.util.ArrayDeque; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.Callable; -import java.util.concurrent.CancellationException; -import java.util.concurrent.Executor; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executors; -import java.util.concurrent.FutureTask; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; - -/** - * ************************************* - * Copied from JB release framework: - * https://android.googlesource.com/platform/frameworks/base/+/jb-release/core/java/android/os/AsyncTask.java - * - * so that threading behavior on all OS versions is the same and we can tweak behavior by using - * executeOnExecutor() if needed. - * - * There are 3 changes in this copy of AsyncTask: - * -pre-HC a single thread executor is used for serial operation - * (Executors.newSingleThreadExecutor) and is the default - * -the default THREAD_POOL_EXECUTOR was changed to use DiscardOldestPolicy - * -a new fixed thread pool called DUAL_THREAD_EXECUTOR was added - * ************************************* - * - *

AsyncTask enables proper and easy use of the UI thread. This class allows to - * perform background operations and publish results on the UI thread without - * having to manipulate threads and/or handlers.

- * - *

AsyncTask is designed to be a helper class around {@link Thread} and {@link Handler} - * and does not constitute a generic threading framework. AsyncTasks should ideally be - * used for short operations (a few seconds at the most.) If you need to keep threads - * running for long periods of time, it is highly recommended you use the various APIs - * provided by the java.util.concurrent pacakge such as {@link Executor}, - * {@link ThreadPoolExecutor} and {@link FutureTask}.

- * - *

An asynchronous task is defined by a computation that runs on a background thread and - * whose result is published on the UI thread. An asynchronous task is defined by 3 generic - * types, called Params, Progress and Result, - * and 4 steps, called onPreExecute, doInBackground, - * onProgressUpdate and onPostExecute.

- * - *
- *

Developer Guides

- *

For more information about using tasks and threads, read the - * Processes and - * Threads developer guide.

- *
- * - *

Usage

- *

AsyncTask must be subclassed to be used. The subclass will override at least - * one method ({@link #doInBackground}), and most often will override a - * second one ({@link #onPostExecute}.)

- * - *

Here is an example of subclassing:

- *
- * private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
- *     protected Long doInBackground(URL... urls) {
- *         int count = urls.length;
- *         long totalSize = 0;
- *         for (int i = 0; i < count; i++) {
- *             totalSize += Downloader.downloadFile(urls[i]);
- *             publishProgress((int) ((i / (float) count) * 100));
- *             // Escape early if cancel() is called
- *             if (isCancelled()) break;
- *         }
- *         return totalSize;
- *     }
- *
- *     protected void onProgressUpdate(Integer... progress) {
- *         setProgressPercent(progress[0]);
- *     }
- *
- *     protected void onPostExecute(Long result) {
- *         showDialog("Downloaded " + result + " bytes");
- *     }
- * }
- * 
- * - *

Once created, a task is executed very simply:

- *
- * new DownloadFilesTask().execute(url1, url2, url3);
- * 
- * - *

AsyncTask's generic types

- *

The three types used by an asynchronous task are the following:

- *
    - *
  1. Params, the type of the parameters sent to the task upon - * execution.
  2. - *
  3. Progress, the type of the progress units published during - * the background computation.
  4. - *
  5. Result, the type of the result of the background - * computation.
  6. - *
- *

Not all types are always used by an asynchronous task. To mark a type as unused, - * simply use the type {@link Void}:

- *
- * private class MyTask extends AsyncTask<Void, Void, Void> { ... }
- * 
- * - *

The 4 steps

- *

When an asynchronous task is executed, the task goes through 4 steps:

- *
    - *
  1. {@link #onPreExecute()}, invoked on the UI thread immediately after the task - * is executed. This step is normally used to setup the task, for instance by - * showing a progress bar in the user interface.
  2. - *
  3. {@link #doInBackground}, invoked on the background thread - * immediately after {@link #onPreExecute()} finishes executing. This step is used - * to perform background computation that can take a long time. The parameters - * of the asynchronous task are passed to this step. The result of the computation must - * be returned by this step and will be passed back to the last step. This step - * can also use {@link #publishProgress} to publish one or more units - * of progress. These values are published on the UI thread, in the - * {@link #onProgressUpdate} step.
  4. - *
  5. {@link #onProgressUpdate}, invoked on the UI thread after a - * call to {@link #publishProgress}. The timing of the execution is - * undefined. This method is used to display any form of progress in the user - * interface while the background computation is still executing. For instance, - * it can be used to animate a progress bar or show logs in a text field.
  6. - *
  7. {@link #onPostExecute}, invoked on the UI thread after the background - * computation finishes. The result of the background computation is passed to - * this step as a parameter.
  8. - *
- * - *

Cancelling a task

- *

A task can be cancelled at any time by invoking {@link #cancel(boolean)}. Invoking - * this method will cause subsequent calls to {@link #isCancelled()} to return true. - * After invoking this method, {@link #onCancelled(Object)}, instead of - * {@link #onPostExecute(Object)} will be invoked after {@link #doInBackground(Object[])} - * returns. To ensure that a task is cancelled as quickly as possible, you should always - * check the return value of {@link #isCancelled()} periodically from - * {@link #doInBackground(Object[])}, if possible (inside a loop for instance.)

- * - *

Threading rules

- *

There are a few threading rules that must be followed for this class to - * work properly:

- * - * - *

Memory observability

- *

AsyncTask guarantees that all callback calls are synchronized in such a way that the following - * operations are safe without explicit synchronizations.

- * - * - *

Order of execution

- *

When first introduced, AsyncTasks were executed serially on a single background - * thread. Starting with {@link android.os.Build.VERSION_CODES#DONUT}, this was changed - * to a pool of threads allowing multiple tasks to operate in parallel. Starting with - * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, tasks are executed on a single - * thread to avoid common application errors caused by parallel execution.

- *

If you truly want parallel execution, you can invoke - * {@link #executeOnExecutor(java.util.concurrent.Executor, Object[])} with - * {@link #THREAD_POOL_EXECUTOR}.

- */ -public abstract class AsyncTask { - private static final String LOG_TAG = "AsyncTask"; - - private static final int CORE_POOL_SIZE = 5; - private static final int MAXIMUM_POOL_SIZE = 128; - private static final int KEEP_ALIVE = 1; - - private static final ThreadFactory sThreadFactory = new ThreadFactory() { - private final AtomicInteger mCount = new AtomicInteger(1); - - public Thread newThread(Runnable r) { - return new Thread(r, "AsyncTask #" + mCount.getAndIncrement()); - } - }; - - private static final BlockingQueue sPoolWorkQueue = - new LinkedBlockingQueue(10); - - /** - * An {@link Executor} that can be used to execute tasks in parallel. - */ - public static final Executor THREAD_POOL_EXECUTOR - = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, - TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory, - new ThreadPoolExecutor.DiscardOldestPolicy()); - - /** - * An {@link Executor} that executes tasks one at a time in serial - * order. This serialization is global to a particular process. - */ - public static final Executor SERIAL_EXECUTOR = Utils.hasHoneycomb() ? new SerialExecutor() : - Executors.newSingleThreadExecutor(sThreadFactory); - - public static final Executor DUAL_THREAD_EXECUTOR = - Executors.newFixedThreadPool(2, sThreadFactory); - - private static final int MESSAGE_POST_RESULT = 0x1; - private static final int MESSAGE_POST_PROGRESS = 0x2; - - private static final InternalHandler sHandler = new InternalHandler(); - - private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR; - private final WorkerRunnable mWorker; - private final FutureTask mFuture; - - private volatile Status mStatus = Status.PENDING; - - private final AtomicBoolean mCancelled = new AtomicBoolean(); - private final AtomicBoolean mTaskInvoked = new AtomicBoolean(); - - @TargetApi(11) - private static class SerialExecutor implements Executor { - final ArrayDeque mTasks = new ArrayDeque(); - Runnable mActive; - - public synchronized void execute(final Runnable r) { - mTasks.offer(new Runnable() { - public void run() { - try { - r.run(); - } finally { - scheduleNext(); - } - } - }); - if (mActive == null) { - scheduleNext(); - } - } - - protected synchronized void scheduleNext() { - if ((mActive = mTasks.poll()) != null) { - THREAD_POOL_EXECUTOR.execute(mActive); - } - } - } - - /** - * Indicates the current status of the task. Each status will be set only once - * during the lifetime of a task. - */ - public enum Status { - /** - * Indicates that the task has not been executed yet. - */ - PENDING, - /** - * Indicates that the task is running. - */ - RUNNING, - /** - * Indicates that {@link AsyncTask#onPostExecute} has finished. - */ - FINISHED, - } - - /** @hide Used to force static handler to be created. */ - public static void init() { - sHandler.getLooper(); - } - - /** @hide */ - public static void setDefaultExecutor(Executor exec) { - sDefaultExecutor = exec; - } - - /** - * Creates a new asynchronous task. This constructor must be invoked on the UI thread. - */ - public AsyncTask() { - mWorker = new WorkerRunnable() { - public Result call() throws Exception { - mTaskInvoked.set(true); - - Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); - //noinspection unchecked - return postResult(doInBackground(mParams)); - } - }; - - mFuture = new FutureTask(mWorker) { - @Override - protected void done() { - try { - postResultIfNotInvoked(get()); - } catch (InterruptedException e) { - android.util.Log.w(LOG_TAG, e); - } catch (ExecutionException e) { - throw new RuntimeException("An error occured while executing doInBackground()", - e.getCause()); - } catch (CancellationException e) { - postResultIfNotInvoked(null); - } - } - }; - } - - private void postResultIfNotInvoked(Result result) { - final boolean wasTaskInvoked = mTaskInvoked.get(); - if (!wasTaskInvoked) { - postResult(result); - } - } - - private Result postResult(Result result) { - @SuppressWarnings("unchecked") - Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT, - new AsyncTaskResult(this, result)); - message.sendToTarget(); - return result; - } - - /** - * Returns the current status of this task. - * - * @return The current status. - */ - public final Status getStatus() { - return mStatus; - } - - /** - * Override this method to perform a computation on a background thread. The - * specified parameters are the parameters passed to {@link #execute} - * by the caller of this task. - * - * This method can call {@link #publishProgress} to publish updates - * on the UI thread. - * - * @param params The parameters of the task. - * - * @return A result, defined by the subclass of this task. - * - * @see #onPreExecute() - * @see #onPostExecute - * @see #publishProgress - */ - protected abstract Result doInBackground(Params... params); - - /** - * Runs on the UI thread before {@link #doInBackground}. - * - * @see #onPostExecute - * @see #doInBackground - */ - protected void onPreExecute() { - } - - /** - *

Runs on the UI thread after {@link #doInBackground}. The - * specified result is the value returned by {@link #doInBackground}.

- * - *

This method won't be invoked if the task was cancelled.

- * - * @param result The result of the operation computed by {@link #doInBackground}. - * - * @see #onPreExecute - * @see #doInBackground - * @see #onCancelled(Object) - */ - @SuppressWarnings({"UnusedDeclaration"}) - protected void onPostExecute(Result result) { - } - - /** - * Runs on the UI thread after {@link #publishProgress} is invoked. - * The specified values are the values passed to {@link #publishProgress}. - * - * @param values The values indicating progress. - * - * @see #publishProgress - * @see #doInBackground - */ - @SuppressWarnings({"UnusedDeclaration"}) - protected void onProgressUpdate(Progress... values) { - } - - /** - *

Runs on the UI thread after {@link #cancel(boolean)} is invoked and - * {@link #doInBackground(Object[])} has finished.

- * - *

The default implementation simply invokes {@link #onCancelled()} and - * ignores the result. If you write your own implementation, do not call - * super.onCancelled(result).

- * - * @param result The result, if any, computed in - * {@link #doInBackground(Object[])}, can be null - * - * @see #cancel(boolean) - * @see #isCancelled() - */ - @SuppressWarnings({"UnusedParameters"}) - protected void onCancelled(Result result) { - onCancelled(); - } - - /** - *

Applications should preferably override {@link #onCancelled(Object)}. - * This method is invoked by the default implementation of - * {@link #onCancelled(Object)}.

- * - *

Runs on the UI thread after {@link #cancel(boolean)} is invoked and - * {@link #doInBackground(Object[])} has finished.

- * - * @see #onCancelled(Object) - * @see #cancel(boolean) - * @see #isCancelled() - */ - protected void onCancelled() { - } - - /** - * Returns true if this task was cancelled before it completed - * normally. If you are calling {@link #cancel(boolean)} on the task, - * the value returned by this method should be checked periodically from - * {@link #doInBackground(Object[])} to end the task as soon as possible. - * - * @return true if task was cancelled before it completed - * - * @see #cancel(boolean) - */ - public final boolean isCancelled() { - return mCancelled.get(); - } - - /** - *

Attempts to cancel execution of this task. This attempt will - * fail if the task has already completed, already been cancelled, - * or could not be cancelled for some other reason. If successful, - * and this task has not started when cancel is called, - * this task should never run. If the task has already started, - * then the mayInterruptIfRunning parameter determines - * whether the thread executing this task should be interrupted in - * an attempt to stop the task.

- * - *

Calling this method will result in {@link #onCancelled(Object)} being - * invoked on the UI thread after {@link #doInBackground(Object[])} - * returns. Calling this method guarantees that {@link #onPostExecute(Object)} - * is never invoked. After invoking this method, you should check the - * value returned by {@link #isCancelled()} periodically from - * {@link #doInBackground(Object[])} to finish the task as early as - * possible.

- * - * @param mayInterruptIfRunning true if the thread executing this - * task should be interrupted; otherwise, in-progress tasks are allowed - * to complete. - * - * @return false if the task could not be cancelled, - * typically because it has already completed normally; - * true otherwise - * - * @see #isCancelled() - * @see #onCancelled(Object) - */ - public final boolean cancel(boolean mayInterruptIfRunning) { - mCancelled.set(true); - return mFuture.cancel(mayInterruptIfRunning); - } - - /** - * Waits if necessary for the computation to complete, and then - * retrieves its result. - * - * @return The computed result. - * - * @throws CancellationException If the computation was cancelled. - * @throws ExecutionException If the computation threw an exception. - * @throws InterruptedException If the current thread was interrupted - * while waiting. - */ - public final Result get() throws InterruptedException, ExecutionException { - return mFuture.get(); - } - - /** - * Waits if necessary for at most the given time for the computation - * to complete, and then retrieves its result. - * - * @param timeout Time to wait before cancelling the operation. - * @param unit The time unit for the timeout. - * - * @return The computed result. - * - * @throws CancellationException If the computation was cancelled. - * @throws ExecutionException If the computation threw an exception. - * @throws InterruptedException If the current thread was interrupted - * while waiting. - * @throws TimeoutException If the wait timed out. - */ - public final Result get(long timeout, TimeUnit unit) throws InterruptedException, - ExecutionException, TimeoutException { - return mFuture.get(timeout, unit); - } - - /** - * Executes the task with the specified parameters. The task returns - * itself (this) so that the caller can keep a reference to it. - * - *

Note: this function schedules the task on a queue for a single background - * thread or pool of threads depending on the platform version. When first - * introduced, AsyncTasks were executed serially on a single background thread. - * Starting with {@link android.os.Build.VERSION_CODES#DONUT}, this was changed - * to a pool of threads allowing multiple tasks to operate in parallel. Starting - * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, tasks are back to being - * executed on a single thread to avoid common application errors caused - * by parallel execution. If you truly want parallel execution, you can use - * the {@link #executeOnExecutor} version of this method - * with {@link #THREAD_POOL_EXECUTOR}; however, see commentary there for warnings - * on its use. - * - *

This method must be invoked on the UI thread. - * - * @param params The parameters of the task. - * - * @return This instance of AsyncTask. - * - * @throws IllegalStateException If {@link #getStatus()} returns either - * {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}. - * - * @see #executeOnExecutor(java.util.concurrent.Executor, Object[]) - * @see #execute(Runnable) - */ - public final AsyncTask execute(Params... params) { - return executeOnExecutor(sDefaultExecutor, params); - } - - /** - * Executes the task with the specified parameters. The task returns - * itself (this) so that the caller can keep a reference to it. - * - *

This method is typically used with {@link #THREAD_POOL_EXECUTOR} to - * allow multiple tasks to run in parallel on a pool of threads managed by - * AsyncTask, however you can also use your own {@link Executor} for custom - * behavior. - * - *

Warning: Allowing multiple tasks to run in parallel from - * a thread pool is generally not what one wants, because the order - * of their operation is not defined. For example, if these tasks are used - * to modify any state in common (such as writing a file due to a button click), - * there are no guarantees on the order of the modifications. - * Without careful work it is possible in rare cases for the newer version - * of the data to be over-written by an older one, leading to obscure data - * loss and stability issues. Such changes are best - * executed in serial; to guarantee such work is serialized regardless of - * platform version you can use this function with {@link #SERIAL_EXECUTOR}. - * - *

This method must be invoked on the UI thread. - * - * @param exec The executor to use. {@link #THREAD_POOL_EXECUTOR} is available as a - * convenient process-wide thread pool for tasks that are loosely coupled. - * @param params The parameters of the task. - * - * @return This instance of AsyncTask. - * - * @throws IllegalStateException If {@link #getStatus()} returns either - * {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}. - * - * @see #execute(Object[]) - */ - public final AsyncTask executeOnExecutor(Executor exec, - Params... params) { - if (mStatus != Status.PENDING) { - switch (mStatus) { - case RUNNING: - throw new IllegalStateException("Cannot execute task:" - + " the task is already running."); - case FINISHED: - throw new IllegalStateException("Cannot execute task:" - + " the task has already been executed " - + "(a task can be executed only once)"); - } - } - - mStatus = Status.RUNNING; - - onPreExecute(); - - mWorker.mParams = params; - exec.execute(mFuture); - - return this; - } - - /** - * Convenience version of {@link #execute(Object...)} for use with - * a simple Runnable object. See {@link #execute(Object[])} for more - * information on the order of execution. - * - * @see #execute(Object[]) - * @see #executeOnExecutor(java.util.concurrent.Executor, Object[]) - */ - public static void execute(Runnable runnable) { - sDefaultExecutor.execute(runnable); - } - - /** - * This method can be invoked from {@link #doInBackground} to - * publish updates on the UI thread while the background computation is - * still running. Each call to this method will trigger the execution of - * {@link #onProgressUpdate} on the UI thread. - * - * {@link #onProgressUpdate} will note be called if the task has been - * canceled. - * - * @param values The progress values to update the UI with. - * - * @see #onProgressUpdate - * @see #doInBackground - */ - protected final void publishProgress(Progress... values) { - if (!isCancelled()) { - sHandler.obtainMessage(MESSAGE_POST_PROGRESS, - new AsyncTaskResult(this, values)).sendToTarget(); - } - } - - private void finish(Result result) { - if (isCancelled()) { - onCancelled(result); - } else { - onPostExecute(result); - } - mStatus = Status.FINISHED; - } - - private static class InternalHandler extends Handler { - @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"}) - @Override - public void handleMessage(Message msg) { - AsyncTaskResult result = (AsyncTaskResult) msg.obj; - switch (msg.what) { - case MESSAGE_POST_RESULT: - // There is only one result - result.mTask.finish(result.mData[0]); - break; - case MESSAGE_POST_PROGRESS: - result.mTask.onProgressUpdate(result.mData); - break; - } - } - } - - private static abstract class WorkerRunnable implements Callable { - Params[] mParams; - } - - @SuppressWarnings({"RawUseOfParameterizedType"}) - private static class AsyncTaskResult { - final AsyncTask mTask; - final Data[] mData; - - AsyncTaskResult(AsyncTask task, Data... data) { - mTask = task; - mData = data; - } - } -} \ No newline at end of file diff --git a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/DiskLruCache.java b/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/DiskLruCache.java deleted file mode 100644 index 26cdbd781..000000000 --- a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/DiskLruCache.java +++ /dev/null @@ -1,953 +0,0 @@ -/* - * Copyright (C) 2011 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.example.android.bitmapfun.util; - -import java.io.BufferedInputStream; -import java.io.BufferedWriter; -import java.io.Closeable; -import java.io.EOFException; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.FileWriter; -import java.io.FilterOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.Reader; -import java.io.StringWriter; -import java.io.Writer; -import java.lang.reflect.Array; -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; - -/** - ****************************************************************************** - * Taken from the JB source code, can be found in: - * libcore/luni/src/main/java/libcore/io/DiskLruCache.java - * or direct link: - * https://android.googlesource.com/platform/libcore/+/android-4.1.1_r1/luni/src/main/java/libcore/io/DiskLruCache.java - ****************************************************************************** - * - * A cache that uses a bounded amount of space on a filesystem. Each cache - * entry has a string key and a fixed number of values. Values are byte - * sequences, accessible as streams or files. Each value must be between {@code - * 0} and {@code Integer.MAX_VALUE} bytes in length. - * - *

The cache stores its data in a directory on the filesystem. This - * directory must be exclusive to the cache; the cache may delete or overwrite - * files from its directory. It is an error for multiple processes to use the - * same cache directory at the same time. - * - *

This cache limits the number of bytes that it will store on the - * filesystem. When the number of stored bytes exceeds the limit, the cache will - * remove entries in the background until the limit is satisfied. The limit is - * not strict: the cache may temporarily exceed it while waiting for files to be - * deleted. The limit does not include filesystem overhead or the cache - * journal so space-sensitive applications should set a conservative limit. - * - *

Clients call {@link #edit} to create or update the values of an entry. An - * entry may have only one editor at one time; if a value is not available to be - * edited then {@link #edit} will return null. - *

    - *
  • When an entry is being created it is necessary to - * supply a full set of values; the empty value should be used as a - * placeholder if necessary. - *
  • When an entry is being edited, it is not necessary - * to supply data for every value; values default to their previous - * value. - *
- * Every {@link #edit} call must be matched by a call to {@link Editor#commit} - * or {@link Editor#abort}. Committing is atomic: a read observes the full set - * of values as they were before or after the commit, but never a mix of values. - * - *

Clients call {@link #get} to read a snapshot of an entry. The read will - * observe the value at the time that {@link #get} was called. Updates and - * removals after the call do not impact ongoing reads. - * - *

This class is tolerant of some I/O errors. If files are missing from the - * filesystem, the corresponding entries will be dropped from the cache. If - * an error occurs while writing a cache value, the edit will fail silently. - * Callers should handle other problems by catching {@code IOException} and - * responding appropriately. - */ -public final class DiskLruCache implements Closeable { - static final String JOURNAL_FILE = "journal"; - static final String JOURNAL_FILE_TMP = "journal.tmp"; - static final String MAGIC = "libcore.io.DiskLruCache"; - static final String VERSION_1 = "1"; - static final long ANY_SEQUENCE_NUMBER = -1; - private static final String CLEAN = "CLEAN"; - private static final String DIRTY = "DIRTY"; - private static final String REMOVE = "REMOVE"; - private static final String READ = "READ"; - - private static final Charset UTF_8 = Charset.forName("UTF-8"); - private static final int IO_BUFFER_SIZE = 8 * 1024; - - /* - * This cache uses a journal file named "journal". A typical journal file - * looks like this: - * libcore.io.DiskLruCache - * 1 - * 100 - * 2 - * - * CLEAN 3400330d1dfc7f3f7f4b8d4d803dfcf6 832 21054 - * DIRTY 335c4c6028171cfddfbaae1a9c313c52 - * CLEAN 335c4c6028171cfddfbaae1a9c313c52 3934 2342 - * REMOVE 335c4c6028171cfddfbaae1a9c313c52 - * DIRTY 1ab96a171faeeee38496d8b330771a7a - * CLEAN 1ab96a171faeeee38496d8b330771a7a 1600 234 - * READ 335c4c6028171cfddfbaae1a9c313c52 - * READ 3400330d1dfc7f3f7f4b8d4d803dfcf6 - * - * The first five lines of the journal form its header. They are the - * constant string "libcore.io.DiskLruCache", the disk cache's version, - * the application's version, the value count, and a blank line. - * - * Each of the subsequent lines in the file is a record of the state of a - * cache entry. Each line contains space-separated values: a state, a key, - * and optional state-specific values. - * o DIRTY lines track that an entry is actively being created or updated. - * Every successful DIRTY action should be followed by a CLEAN or REMOVE - * action. DIRTY lines without a matching CLEAN or REMOVE indicate that - * temporary files may need to be deleted. - * o CLEAN lines track a cache entry that has been successfully published - * and may be read. A publish line is followed by the lengths of each of - * its values. - * o READ lines track accesses for LRU. - * o REMOVE lines track entries that have been deleted. - * - * The journal file is appended to as cache operations occur. The journal may - * occasionally be compacted by dropping redundant lines. A temporary file named - * "journal.tmp" will be used during compaction; that file should be deleted if - * it exists when the cache is opened. - */ - - private final File directory; - private final File journalFile; - private final File journalFileTmp; - private final int appVersion; - private final long maxSize; - private final int valueCount; - private long size = 0; - private Writer journalWriter; - private final LinkedHashMap lruEntries - = new LinkedHashMap(0, 0.75f, true); - private int redundantOpCount; - - /** - * To differentiate between old and current snapshots, each entry is given - * a sequence number each time an edit is committed. A snapshot is stale if - * its sequence number is not equal to its entry's sequence number. - */ - private long nextSequenceNumber = 0; - - /* From java.util.Arrays */ - @SuppressWarnings("unchecked") - private static T[] copyOfRange(T[] original, int start, int end) { - final int originalLength = original.length; // For exception priority compatibility. - if (start > end) { - throw new IllegalArgumentException(); - } - if (start < 0 || start > originalLength) { - throw new ArrayIndexOutOfBoundsException(); - } - final int resultLength = end - start; - final int copyLength = Math.min(resultLength, originalLength - start); - final T[] result = (T[]) Array - .newInstance(original.getClass().getComponentType(), resultLength); - System.arraycopy(original, start, result, 0, copyLength); - return result; - } - - /** - * Returns the remainder of 'reader' as a string, closing it when done. - */ - public static String readFully(Reader reader) throws IOException { - try { - StringWriter writer = new StringWriter(); - char[] buffer = new char[1024]; - int count; - while ((count = reader.read(buffer)) != -1) { - writer.write(buffer, 0, count); - } - return writer.toString(); - } finally { - reader.close(); - } - } - - /** - * Returns the ASCII characters up to but not including the next "\r\n", or - * "\n". - * - * @throws java.io.EOFException if the stream is exhausted before the next newline - * character. - */ - public static String readAsciiLine(InputStream in) throws IOException { - // TODO: support UTF-8 here instead - - StringBuilder result = new StringBuilder(80); - while (true) { - int c = in.read(); - if (c == -1) { - throw new EOFException(); - } else if (c == '\n') { - break; - } - - result.append((char) c); - } - int length = result.length(); - if (length > 0 && result.charAt(length - 1) == '\r') { - result.setLength(length - 1); - } - return result.toString(); - } - - /** - * Closes 'closeable', ignoring any checked exceptions. Does nothing if 'closeable' is null. - */ - public static void closeQuietly(Closeable closeable) { - if (closeable != null) { - try { - closeable.close(); - } catch (RuntimeException rethrown) { - throw rethrown; - } catch (Exception ignored) { - } - } - } - - /** - * Recursively delete everything in {@code dir}. - */ - // TODO: this should specify paths as Strings rather than as Files - public static void deleteContents(File dir) throws IOException { - File[] files = dir.listFiles(); - if (files == null) { - throw new IllegalArgumentException("not a directory: " + dir); - } - for (File file : files) { - if (file.isDirectory()) { - deleteContents(file); - } - if (!file.delete()) { - throw new IOException("failed to delete file: " + file); - } - } - } - - /** This cache uses a single background thread to evict entries. */ - private final ExecutorService executorService = new ThreadPoolExecutor(0, 1, - 60L, TimeUnit.SECONDS, new LinkedBlockingQueue()); - private final Callable cleanupCallable = new Callable() { - @Override public Void call() throws Exception { - synchronized (DiskLruCache.this) { - if (journalWriter == null) { - return null; // closed - } - trimToSize(); - if (journalRebuildRequired()) { - rebuildJournal(); - redundantOpCount = 0; - } - } - return null; - } - }; - - private DiskLruCache(File directory, int appVersion, int valueCount, long maxSize) { - this.directory = directory; - this.appVersion = appVersion; - this.journalFile = new File(directory, JOURNAL_FILE); - this.journalFileTmp = new File(directory, JOURNAL_FILE_TMP); - this.valueCount = valueCount; - this.maxSize = maxSize; - } - - /** - * Opens the cache in {@code directory}, creating a cache if none exists - * there. - * - * @param directory a writable directory - * @param appVersion - * @param valueCount the number of values per cache entry. Must be positive. - * @param maxSize the maximum number of bytes this cache should use to store - * @throws IOException if reading or writing the cache directory fails - */ - public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize) - throws IOException { - if (maxSize <= 0) { - throw new IllegalArgumentException("maxSize <= 0"); - } - if (valueCount <= 0) { - throw new IllegalArgumentException("valueCount <= 0"); - } - - // prefer to pick up where we left off - DiskLruCache cache = new DiskLruCache(directory, appVersion, valueCount, maxSize); - if (cache.journalFile.exists()) { - try { - cache.readJournal(); - cache.processJournal(); - cache.journalWriter = new BufferedWriter(new FileWriter(cache.journalFile, true), - IO_BUFFER_SIZE); - return cache; - } catch (IOException journalIsCorrupt) { -// System.logW("DiskLruCache " + directory + " is corrupt: " -// + journalIsCorrupt.getMessage() + ", removing"); - cache.delete(); - } - } - - // create a new empty cache - directory.mkdirs(); - cache = new DiskLruCache(directory, appVersion, valueCount, maxSize); - cache.rebuildJournal(); - return cache; - } - - private void readJournal() throws IOException { - InputStream in = new BufferedInputStream(new FileInputStream(journalFile), IO_BUFFER_SIZE); - try { - String magic = readAsciiLine(in); - String version = readAsciiLine(in); - String appVersionString = readAsciiLine(in); - String valueCountString = readAsciiLine(in); - String blank = readAsciiLine(in); - if (!MAGIC.equals(magic) - || !VERSION_1.equals(version) - || !Integer.toString(appVersion).equals(appVersionString) - || !Integer.toString(valueCount).equals(valueCountString) - || !"".equals(blank)) { - throw new IOException("unexpected journal header: [" - + magic + ", " + version + ", " + valueCountString + ", " + blank + "]"); - } - - while (true) { - try { - readJournalLine(readAsciiLine(in)); - } catch (EOFException endOfJournal) { - break; - } - } - } finally { - closeQuietly(in); - } - } - - private void readJournalLine(String line) throws IOException { - String[] parts = line.split(" "); - if (parts.length < 2) { - throw new IOException("unexpected journal line: " + line); - } - - String key = parts[1]; - if (parts[0].equals(REMOVE) && parts.length == 2) { - lruEntries.remove(key); - return; - } - - Entry entry = lruEntries.get(key); - if (entry == null) { - entry = new Entry(key); - lruEntries.put(key, entry); - } - - if (parts[0].equals(CLEAN) && parts.length == 2 + valueCount) { - entry.readable = true; - entry.currentEditor = null; - entry.setLengths(copyOfRange(parts, 2, parts.length)); - } else if (parts[0].equals(DIRTY) && parts.length == 2) { - entry.currentEditor = new Editor(entry); - } else if (parts[0].equals(READ) && parts.length == 2) { - // this work was already done by calling lruEntries.get() - } else { - throw new IOException("unexpected journal line: " + line); - } - } - - /** - * Computes the initial size and collects garbage as a part of opening the - * cache. Dirty entries are assumed to be inconsistent and will be deleted. - */ - private void processJournal() throws IOException { - deleteIfExists(journalFileTmp); - for (Iterator i = lruEntries.values().iterator(); i.hasNext(); ) { - Entry entry = i.next(); - if (entry.currentEditor == null) { - for (int t = 0; t < valueCount; t++) { - size += entry.lengths[t]; - } - } else { - entry.currentEditor = null; - for (int t = 0; t < valueCount; t++) { - deleteIfExists(entry.getCleanFile(t)); - deleteIfExists(entry.getDirtyFile(t)); - } - i.remove(); - } - } - } - - /** - * Creates a new journal that omits redundant information. This replaces the - * current journal if it exists. - */ - private synchronized void rebuildJournal() throws IOException { - if (journalWriter != null) { - journalWriter.close(); - } - - Writer writer = new BufferedWriter(new FileWriter(journalFileTmp), IO_BUFFER_SIZE); - writer.write(MAGIC); - writer.write("\n"); - writer.write(VERSION_1); - writer.write("\n"); - writer.write(Integer.toString(appVersion)); - writer.write("\n"); - writer.write(Integer.toString(valueCount)); - writer.write("\n"); - writer.write("\n"); - - for (Entry entry : lruEntries.values()) { - if (entry.currentEditor != null) { - writer.write(DIRTY + ' ' + entry.key + '\n'); - } else { - writer.write(CLEAN + ' ' + entry.key + entry.getLengths() + '\n'); - } - } - - writer.close(); - journalFileTmp.renameTo(journalFile); - journalWriter = new BufferedWriter(new FileWriter(journalFile, true), IO_BUFFER_SIZE); - } - - private static void deleteIfExists(File file) throws IOException { -// try { -// Libcore.os.remove(file.getPath()); -// } catch (ErrnoException errnoException) { -// if (errnoException.errno != OsConstants.ENOENT) { -// throw errnoException.rethrowAsIOException(); -// } -// } - if (file.exists() && !file.delete()) { - throw new IOException(); - } - } - - /** - * Returns a snapshot of the entry named {@code key}, or null if it doesn't - * exist is not currently readable. If a value is returned, it is moved to - * the head of the LRU queue. - */ - public synchronized Snapshot get(String key) throws IOException { - checkNotClosed(); - validateKey(key); - Entry entry = lruEntries.get(key); - if (entry == null) { - return null; - } - - if (!entry.readable) { - return null; - } - - /* - * Open all streams eagerly to guarantee that we see a single published - * snapshot. If we opened streams lazily then the streams could come - * from different edits. - */ - InputStream[] ins = new InputStream[valueCount]; - try { - for (int i = 0; i < valueCount; i++) { - ins[i] = new FileInputStream(entry.getCleanFile(i)); - } - } catch (FileNotFoundException e) { - // a file must have been deleted manually! - return null; - } - - redundantOpCount++; - journalWriter.append(READ + ' ' + key + '\n'); - if (journalRebuildRequired()) { - executorService.submit(cleanupCallable); - } - - return new Snapshot(key, entry.sequenceNumber, ins); - } - - /** - * Returns an editor for the entry named {@code key}, or null if another - * edit is in progress. - */ - public Editor edit(String key) throws IOException { - return edit(key, ANY_SEQUENCE_NUMBER); - } - - private synchronized Editor edit(String key, long expectedSequenceNumber) throws IOException { - checkNotClosed(); - validateKey(key); - Entry entry = lruEntries.get(key); - if (expectedSequenceNumber != ANY_SEQUENCE_NUMBER - && (entry == null || entry.sequenceNumber != expectedSequenceNumber)) { - return null; // snapshot is stale - } - if (entry == null) { - entry = new Entry(key); - lruEntries.put(key, entry); - } else if (entry.currentEditor != null) { - return null; // another edit is in progress - } - - Editor editor = new Editor(entry); - entry.currentEditor = editor; - - // flush the journal before creating files to prevent file leaks - journalWriter.write(DIRTY + ' ' + key + '\n'); - journalWriter.flush(); - return editor; - } - - /** - * Returns the directory where this cache stores its data. - */ - public File getDirectory() { - return directory; - } - - /** - * Returns the maximum number of bytes that this cache should use to store - * its data. - */ - public long maxSize() { - return maxSize; - } - - /** - * Returns the number of bytes currently being used to store the values in - * this cache. This may be greater than the max size if a background - * deletion is pending. - */ - public synchronized long size() { - return size; - } - - private synchronized void completeEdit(Editor editor, boolean success) throws IOException { - Entry entry = editor.entry; - if (entry.currentEditor != editor) { - throw new IllegalStateException(); - } - - // if this edit is creating the entry for the first time, every index must have a value - if (success && !entry.readable) { - for (int i = 0; i < valueCount; i++) { - if (!entry.getDirtyFile(i).exists()) { - editor.abort(); - throw new IllegalStateException("edit didn't create file " + i); - } - } - } - - for (int i = 0; i < valueCount; i++) { - File dirty = entry.getDirtyFile(i); - if (success) { - if (dirty.exists()) { - File clean = entry.getCleanFile(i); - dirty.renameTo(clean); - long oldLength = entry.lengths[i]; - long newLength = clean.length(); - entry.lengths[i] = newLength; - size = size - oldLength + newLength; - } - } else { - deleteIfExists(dirty); - } - } - - redundantOpCount++; - entry.currentEditor = null; - if (entry.readable | success) { - entry.readable = true; - journalWriter.write(CLEAN + ' ' + entry.key + entry.getLengths() + '\n'); - if (success) { - entry.sequenceNumber = nextSequenceNumber++; - } - } else { - lruEntries.remove(entry.key); - journalWriter.write(REMOVE + ' ' + entry.key + '\n'); - } - - if (size > maxSize || journalRebuildRequired()) { - executorService.submit(cleanupCallable); - } - } - - /** - * We only rebuild the journal when it will halve the size of the journal - * and eliminate at least 2000 ops. - */ - private boolean journalRebuildRequired() { - final int REDUNDANT_OP_COMPACT_THRESHOLD = 2000; - return redundantOpCount >= REDUNDANT_OP_COMPACT_THRESHOLD - && redundantOpCount >= lruEntries.size(); - } - - /** - * Drops the entry for {@code key} if it exists and can be removed. Entries - * actively being edited cannot be removed. - * - * @return true if an entry was removed. - */ - public synchronized boolean remove(String key) throws IOException { - checkNotClosed(); - validateKey(key); - Entry entry = lruEntries.get(key); - if (entry == null || entry.currentEditor != null) { - return false; - } - - for (int i = 0; i < valueCount; i++) { - File file = entry.getCleanFile(i); - if (!file.delete()) { - throw new IOException("failed to delete " + file); - } - size -= entry.lengths[i]; - entry.lengths[i] = 0; - } - - redundantOpCount++; - journalWriter.append(REMOVE + ' ' + key + '\n'); - lruEntries.remove(key); - - if (journalRebuildRequired()) { - executorService.submit(cleanupCallable); - } - - return true; - } - - /** - * Returns true if this cache has been closed. - */ - public boolean isClosed() { - return journalWriter == null; - } - - private void checkNotClosed() { - if (journalWriter == null) { - throw new IllegalStateException("cache is closed"); - } - } - - /** - * Force buffered operations to the filesystem. - */ - public synchronized void flush() throws IOException { - checkNotClosed(); - trimToSize(); - journalWriter.flush(); - } - - /** - * Closes this cache. Stored values will remain on the filesystem. - */ - public synchronized void close() throws IOException { - if (journalWriter == null) { - return; // already closed - } - for (Entry entry : new ArrayList(lruEntries.values())) { - if (entry.currentEditor != null) { - entry.currentEditor.abort(); - } - } - trimToSize(); - journalWriter.close(); - journalWriter = null; - } - - private void trimToSize() throws IOException { - while (size > maxSize) { -// Map.Entry toEvict = lruEntries.eldest(); - final Map.Entry toEvict = lruEntries.entrySet().iterator().next(); - remove(toEvict.getKey()); - } - } - - /** - * Closes the cache and deletes all of its stored values. This will delete - * all files in the cache directory including files that weren't created by - * the cache. - */ - public void delete() throws IOException { - close(); - deleteContents(directory); - } - - private void validateKey(String key) { - if (key.contains(" ") || key.contains("\n") || key.contains("\r")) { - throw new IllegalArgumentException( - "keys must not contain spaces or newlines: \"" + key + "\""); - } - } - - private static String inputStreamToString(InputStream in) throws IOException { - return readFully(new InputStreamReader(in, UTF_8)); - } - - /** - * A snapshot of the values for an entry. - */ - public final class Snapshot implements Closeable { - private final String key; - private final long sequenceNumber; - private final InputStream[] ins; - - private Snapshot(String key, long sequenceNumber, InputStream[] ins) { - this.key = key; - this.sequenceNumber = sequenceNumber; - this.ins = ins; - } - - /** - * Returns an editor for this snapshot's entry, or null if either the - * entry has changed since this snapshot was created or if another edit - * is in progress. - */ - public Editor edit() throws IOException { - return DiskLruCache.this.edit(key, sequenceNumber); - } - - /** - * Returns the unbuffered stream with the value for {@code index}. - */ - public InputStream getInputStream(int index) { - return ins[index]; - } - - /** - * Returns the string value for {@code index}. - */ - public String getString(int index) throws IOException { - return inputStreamToString(getInputStream(index)); - } - - @Override public void close() { - for (InputStream in : ins) { - closeQuietly(in); - } - } - } - - /** - * Edits the values for an entry. - */ - public final class Editor { - private final Entry entry; - private boolean hasErrors; - - private Editor(Entry entry) { - this.entry = entry; - } - - /** - * Returns an unbuffered input stream to read the last committed value, - * or null if no value has been committed. - */ - public InputStream newInputStream(int index) throws IOException { - synchronized (DiskLruCache.this) { - if (entry.currentEditor != this) { - throw new IllegalStateException(); - } - if (!entry.readable) { - return null; - } - return new FileInputStream(entry.getCleanFile(index)); - } - } - - /** - * Returns the last committed value as a string, or null if no value - * has been committed. - */ - public String getString(int index) throws IOException { - InputStream in = newInputStream(index); - return in != null ? inputStreamToString(in) : null; - } - - /** - * Returns a new unbuffered output stream to write the value at - * {@code index}. If the underlying output stream encounters errors - * when writing to the filesystem, this edit will be aborted when - * {@link #commit} is called. The returned output stream does not throw - * IOExceptions. - */ - public OutputStream newOutputStream(int index) throws IOException { - synchronized (DiskLruCache.this) { - if (entry.currentEditor != this) { - throw new IllegalStateException(); - } - return new FaultHidingOutputStream(new FileOutputStream(entry.getDirtyFile(index))); - } - } - - /** - * Sets the value at {@code index} to {@code value}. - */ - public void set(int index, String value) throws IOException { - Writer writer = null; - try { - writer = new OutputStreamWriter(newOutputStream(index), UTF_8); - writer.write(value); - } finally { - closeQuietly(writer); - } - } - - /** - * Commits this edit so it is visible to readers. This releases the - * edit lock so another edit may be started on the same key. - */ - public void commit() throws IOException { - if (hasErrors) { - completeEdit(this, false); - remove(entry.key); // the previous entry is stale - } else { - completeEdit(this, true); - } - } - - /** - * Aborts this edit. This releases the edit lock so another edit may be - * started on the same key. - */ - public void abort() throws IOException { - completeEdit(this, false); - } - - private class FaultHidingOutputStream extends FilterOutputStream { - private FaultHidingOutputStream(OutputStream out) { - super(out); - } - - @Override public void write(int oneByte) { - try { - out.write(oneByte); - } catch (IOException e) { - hasErrors = true; - } - } - - @Override public void write(byte[] buffer, int offset, int length) { - try { - out.write(buffer, offset, length); - } catch (IOException e) { - hasErrors = true; - } - } - - @Override public void close() { - try { - out.close(); - } catch (IOException e) { - hasErrors = true; - } - } - - @Override public void flush() { - try { - out.flush(); - } catch (IOException e) { - hasErrors = true; - } - } - } - } - - private final class Entry { - private final String key; - - /** Lengths of this entry's files. */ - private final long[] lengths; - - /** True if this entry has ever been published */ - private boolean readable; - - /** The ongoing edit or null if this entry is not being edited. */ - private Editor currentEditor; - - /** The sequence number of the most recently committed edit to this entry. */ - private long sequenceNumber; - - private Entry(String key) { - this.key = key; - this.lengths = new long[valueCount]; - } - - public String getLengths() throws IOException { - StringBuilder result = new StringBuilder(); - for (long size : lengths) { - result.append(' ').append(size); - } - return result.toString(); - } - - /** - * Set lengths using decimal numbers like "10123". - */ - private void setLengths(String[] strings) throws IOException { - if (strings.length != valueCount) { - throw invalidLengths(strings); - } - - try { - for (int i = 0; i < strings.length; i++) { - lengths[i] = Long.parseLong(strings[i]); - } - } catch (NumberFormatException e) { - throw invalidLengths(strings); - } - } - - private IOException invalidLengths(String[] strings) throws IOException { - throw new IOException("unexpected journal line: " + Arrays.toString(strings)); - } - - public File getCleanFile(int i) { - return new File(directory, key + "." + i); - } - - public File getDirtyFile(int i) { - return new File(directory, key + "." + i + ".tmp"); - } - } -} diff --git a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/ImageCache.java b/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/ImageCache.java deleted file mode 100644 index 7021d2c89..000000000 --- a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/ImageCache.java +++ /dev/null @@ -1,725 +0,0 @@ -/* - * Copyright (C) 2012 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.example.android.bitmapfun.util; - -import android.annotation.TargetApi; -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.Bitmap.CompressFormat; -import android.graphics.Bitmap.Config; -import android.graphics.BitmapFactory; -import android.graphics.drawable.BitmapDrawable; -import android.os.Build.VERSION_CODES; -import android.os.Bundle; -import android.os.Environment; -import android.os.StatFs; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentManager; -import android.support.v4.util.LruCache; -import android.util.Log; - -import com.example.android.bitmapfun.BuildConfig; - -import java.io.File; -import java.io.FileDescriptor; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.lang.ref.SoftReference; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.Collections; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Set; - -/** - * This class handles disk and memory caching of bitmaps in conjunction with the - * {@link ImageWorker} class and its subclasses. Use - * {@link ImageCache#getInstance(FragmentManager, ImageCacheParams)} to get an instance of this - * class, although usually a cache should be added directly to an {@link ImageWorker} by calling - * {@link ImageWorker#addImageCache(FragmentManager, ImageCacheParams)}. - */ -public class ImageCache { - private static final String TAG = "ImageCache"; - - // Default memory cache size in kilobytes - private static final int DEFAULT_MEM_CACHE_SIZE = 1024 * 5; // 5MB - - // Default disk cache size in bytes - private static final int DEFAULT_DISK_CACHE_SIZE = 1024 * 1024 * 10; // 10MB - - // Compression settings when writing images to disk cache - private static final CompressFormat DEFAULT_COMPRESS_FORMAT = CompressFormat.JPEG; - private static final int DEFAULT_COMPRESS_QUALITY = 70; - private static final int DISK_CACHE_INDEX = 0; - - // Constants to easily toggle various caches - private static final boolean DEFAULT_MEM_CACHE_ENABLED = true; - private static final boolean DEFAULT_DISK_CACHE_ENABLED = true; - private static final boolean DEFAULT_INIT_DISK_CACHE_ON_CREATE = false; - - private DiskLruCache mDiskLruCache; - private LruCache mMemoryCache; - private ImageCacheParams mCacheParams; - private final Object mDiskCacheLock = new Object(); - private boolean mDiskCacheStarting = true; - - private Set> mReusableBitmaps; - - /** - * Create a new ImageCache object using the specified parameters. This should not be - * called directly by other classes, instead use - * {@link ImageCache#getInstance(FragmentManager, ImageCacheParams)} to fetch an ImageCache - * instance. - * - * @param cacheParams The cache parameters to use to initialize the cache - */ - private ImageCache(ImageCacheParams cacheParams) { - init(cacheParams); - } - - /** - * Return an {@link ImageCache} instance. A {@link RetainFragment} is used to retain the - * ImageCache object across configuration changes such as a change in device orientation. - * - * @param fragmentManager The fragment manager to use when dealing with the retained fragment. - * @param cacheParams The cache parameters to use if the ImageCache needs instantiation. - * @return An existing retained ImageCache object or a new one if one did not exist - */ - public static ImageCache getInstance( - FragmentManager fragmentManager, ImageCacheParams cacheParams) { - - // Search for, or create an instance of the non-UI RetainFragment - final RetainFragment mRetainFragment = findOrCreateRetainFragment(fragmentManager); - - // See if we already have an ImageCache stored in RetainFragment - ImageCache imageCache = (ImageCache) mRetainFragment.getObject(); - - // No existing ImageCache, create one and store it in RetainFragment - if (imageCache == null) { - imageCache = new ImageCache(cacheParams); - mRetainFragment.setObject(imageCache); - } - - return imageCache; - } - - /** - * Initialize the cache, providing all parameters. - * - * @param cacheParams The cache parameters to initialize the cache - */ - private void init(ImageCacheParams cacheParams) { - mCacheParams = cacheParams; - - // Set up memory cache - if (mCacheParams.memoryCacheEnabled) { - if (BuildConfig.DEBUG) { - Log.d(TAG, "Memory cache created (size = " + mCacheParams.memCacheSize + ")"); - } - - // If we're running on Honeycomb or newer, create a set of reusable bitmaps that can be - // populated into the inBitmap field of BitmapFactory.Options. Note that the set is - // of SoftReferences which will actually not be very effective due to the garbage - // collector being aggressive clearing Soft/WeakReferences. A better approach - // would be to use a strongly references bitmaps, however this would require some - // balancing of memory usage between this set and the bitmap LruCache. It would also - // require knowledge of the expected size of the bitmaps. From Honeycomb to JellyBean - // the size would need to be precise, from KitKat onward the size would just need to - // be the upper bound (due to changes in how inBitmap can re-use bitmaps). - if (Utils.hasHoneycomb()) { - mReusableBitmaps = - Collections.synchronizedSet(new HashSet>()); - } - - mMemoryCache = new LruCache(mCacheParams.memCacheSize) { - - /** - * Notify the removed entry that is no longer being cached - */ - @Override - protected void entryRemoved(boolean evicted, String key, - BitmapDrawable oldValue, BitmapDrawable newValue) { - if (RecyclingBitmapDrawable.class.isInstance(oldValue)) { - // The removed entry is a recycling drawable, so notify it - // that it has been removed from the memory cache - ((RecyclingBitmapDrawable) oldValue).setIsCached(false); - } else { - // The removed entry is a standard BitmapDrawable - - if (Utils.hasHoneycomb()) { - // We're running on Honeycomb or later, so add the bitmap - // to a SoftReference set for possible use with inBitmap later - mReusableBitmaps.add(new SoftReference(oldValue.getBitmap())); - } - } - } - - /** - * Measure item size in kilobytes rather than units which is more practical - * for a bitmap cache - */ - @Override - protected int sizeOf(String key, BitmapDrawable value) { - final int bitmapSize = getBitmapSize(value) / 1024; - return bitmapSize == 0 ? 1 : bitmapSize; - } - }; - } - - // By default the disk cache is not initialized here as it should be initialized - // on a separate thread due to disk access. - if (cacheParams.initDiskCacheOnCreate) { - // Set up disk cache - initDiskCache(); - } - } - - /** - * Initializes the disk cache. Note that this includes disk access so this should not be - * executed on the main/UI thread. By default an ImageCache does not initialize the disk - * cache when it is created, instead you should call initDiskCache() to initialize it on a - * background thread. - */ - public void initDiskCache() { - // Set up disk cache - synchronized (mDiskCacheLock) { - if (mDiskLruCache == null || mDiskLruCache.isClosed()) { - File diskCacheDir = mCacheParams.diskCacheDir; - if (mCacheParams.diskCacheEnabled && diskCacheDir != null) { - if (!diskCacheDir.exists()) { - diskCacheDir.mkdirs(); - } - if (getUsableSpace(diskCacheDir) > mCacheParams.diskCacheSize) { - try { - mDiskLruCache = DiskLruCache.open( - diskCacheDir, 1, 1, mCacheParams.diskCacheSize); - if (BuildConfig.DEBUG) { - Log.d(TAG, "Disk cache initialized"); - } - } catch (final IOException e) { - mCacheParams.diskCacheDir = null; - Log.e(TAG, "initDiskCache - " + e); - } - } - } - } - mDiskCacheStarting = false; - mDiskCacheLock.notifyAll(); - } - } - - /** - * Adds a bitmap to both memory and disk cache. - * @param data Unique identifier for the bitmap to store - * @param value The bitmap drawable to store - */ - public void addBitmapToCache(String data, BitmapDrawable value) { - if (data == null || value == null) { - return; - } - - // Add to memory cache - if (mMemoryCache != null) { - if (RecyclingBitmapDrawable.class.isInstance(value)) { - // The removed entry is a recycling drawable, so notify it - // that it has been added into the memory cache - ((RecyclingBitmapDrawable) value).setIsCached(true); - } - mMemoryCache.put(data, value); - } - - synchronized (mDiskCacheLock) { - // Add to disk cache - if (mDiskLruCache != null) { - final String key = hashKeyForDisk(data); - OutputStream out = null; - try { - DiskLruCache.Snapshot snapshot = mDiskLruCache.get(key); - if (snapshot == null) { - final DiskLruCache.Editor editor = mDiskLruCache.edit(key); - if (editor != null) { - out = editor.newOutputStream(DISK_CACHE_INDEX); - value.getBitmap().compress( - mCacheParams.compressFormat, mCacheParams.compressQuality, out); - editor.commit(); - out.close(); - } - } else { - snapshot.getInputStream(DISK_CACHE_INDEX).close(); - } - } catch (final IOException e) { - Log.e(TAG, "addBitmapToCache - " + e); - } catch (Exception e) { - Log.e(TAG, "addBitmapToCache - " + e); - } finally { - try { - if (out != null) { - out.close(); - } - } catch (IOException e) {} - } - } - } - } - - /** - * Get from memory cache. - * - * @param data Unique identifier for which item to get - * @return The bitmap drawable if found in cache, null otherwise - */ - public BitmapDrawable getBitmapFromMemCache(String data) { - BitmapDrawable memValue = null; - - if (mMemoryCache != null) { - memValue = mMemoryCache.get(data); - } - - if (BuildConfig.DEBUG && memValue != null) { - Log.d(TAG, "Memory cache hit"); - } - - return memValue; - } - - /** - * Get from disk cache. - * - * @param data Unique identifier for which item to get - * @return The bitmap if found in cache, null otherwise - */ - public Bitmap getBitmapFromDiskCache(String data) { - final String key = hashKeyForDisk(data); - Bitmap bitmap = null; - - synchronized (mDiskCacheLock) { - while (mDiskCacheStarting) { - try { - mDiskCacheLock.wait(); - } catch (InterruptedException e) {} - } - if (mDiskLruCache != null) { - InputStream inputStream = null; - try { - final DiskLruCache.Snapshot snapshot = mDiskLruCache.get(key); - if (snapshot != null) { - if (BuildConfig.DEBUG) { - Log.d(TAG, "Disk cache hit"); - } - inputStream = snapshot.getInputStream(DISK_CACHE_INDEX); - if (inputStream != null) { - FileDescriptor fd = ((FileInputStream) inputStream).getFD(); - - // Decode bitmap, but we don't want to sample so give - // MAX_VALUE as the target dimensions - bitmap = ImageResizer.decodeSampledBitmapFromDescriptor( - fd, Integer.MAX_VALUE, Integer.MAX_VALUE, this); - } - } - } catch (final IOException e) { - Log.e(TAG, "getBitmapFromDiskCache - " + e); - } finally { - try { - if (inputStream != null) { - inputStream.close(); - } - } catch (IOException e) {} - } - } - return bitmap; - } - } - - /** - * @param options - BitmapFactory.Options with out* options populated - * @return Bitmap that case be used for inBitmap - */ - protected Bitmap getBitmapFromReusableSet(BitmapFactory.Options options) { - Bitmap bitmap = null; - - if (mReusableBitmaps != null && !mReusableBitmaps.isEmpty()) { - synchronized (mReusableBitmaps) { - final Iterator> iterator = mReusableBitmaps.iterator(); - Bitmap item; - - while (iterator.hasNext()) { - item = iterator.next().get(); - - if (null != item && item.isMutable()) { - // Check to see it the item can be used for inBitmap - if (canUseForInBitmap(item, options)) { - bitmap = item; - - // Remove from reusable set so it can't be used again - iterator.remove(); - break; - } - } else { - // Remove from the set if the reference has been cleared. - iterator.remove(); - } - } - } - } - - return bitmap; - } - - /** - * Clears both the memory and disk cache associated with this ImageCache object. Note that - * this includes disk access so this should not be executed on the main/UI thread. - */ - public void clearCache() { - if (mMemoryCache != null) { - mMemoryCache.evictAll(); - if (BuildConfig.DEBUG) { - Log.d(TAG, "Memory cache cleared"); - } - } - - synchronized (mDiskCacheLock) { - mDiskCacheStarting = true; - if (mDiskLruCache != null && !mDiskLruCache.isClosed()) { - try { - mDiskLruCache.delete(); - if (BuildConfig.DEBUG) { - Log.d(TAG, "Disk cache cleared"); - } - } catch (IOException e) { - Log.e(TAG, "clearCache - " + e); - } - mDiskLruCache = null; - initDiskCache(); - } - } - } - - /** - * Flushes the disk cache associated with this ImageCache object. Note that this includes - * disk access so this should not be executed on the main/UI thread. - */ - public void flush() { - synchronized (mDiskCacheLock) { - if (mDiskLruCache != null) { - try { - mDiskLruCache.flush(); - if (BuildConfig.DEBUG) { - Log.d(TAG, "Disk cache flushed"); - } - } catch (IOException e) { - Log.e(TAG, "flush - " + e); - } - } - } - } - - /** - * Closes the disk cache associated with this ImageCache object. Note that this includes - * disk access so this should not be executed on the main/UI thread. - */ - public void close() { - synchronized (mDiskCacheLock) { - if (mDiskLruCache != null) { - try { - if (!mDiskLruCache.isClosed()) { - mDiskLruCache.close(); - mDiskLruCache = null; - if (BuildConfig.DEBUG) { - Log.d(TAG, "Disk cache closed"); - } - } - } catch (IOException e) { - Log.e(TAG, "close - " + e); - } - } - } - } - - /** - * A holder class that contains cache parameters. - */ - public static class ImageCacheParams { - public int memCacheSize = DEFAULT_MEM_CACHE_SIZE; - public int diskCacheSize = DEFAULT_DISK_CACHE_SIZE; - public File diskCacheDir; - public CompressFormat compressFormat = DEFAULT_COMPRESS_FORMAT; - public int compressQuality = DEFAULT_COMPRESS_QUALITY; - public boolean memoryCacheEnabled = DEFAULT_MEM_CACHE_ENABLED; - public boolean diskCacheEnabled = DEFAULT_DISK_CACHE_ENABLED; - public boolean initDiskCacheOnCreate = DEFAULT_INIT_DISK_CACHE_ON_CREATE; - - /** - * Create a set of image cache parameters that can be provided to - * {@link ImageCache#getInstance(FragmentManager, ImageCacheParams)} or - * {@link ImageWorker#addImageCache(FragmentManager, ImageCacheParams)}. - * @param context A context to use. - * @param diskCacheDirectoryName A unique subdirectory name that will be appended to the - * application cache directory. Usually "cache" or "images" - * is sufficient. - */ - public ImageCacheParams(Context context, String diskCacheDirectoryName) { - diskCacheDir = getDiskCacheDir(context, diskCacheDirectoryName); - } - - /** - * Sets the memory cache size based on a percentage of the max available VM memory. - * Eg. setting percent to 0.2 would set the memory cache to one fifth of the available - * memory. Throws {@link IllegalArgumentException} if percent is < 0.01 or > .8. - * memCacheSize is stored in kilobytes instead of bytes as this will eventually be passed - * to construct a LruCache which takes an int in its constructor. - * - * This value should be chosen carefully based on a number of factors - * Refer to the corresponding Android Training class for more discussion: - * http://developer.android.com/training/displaying-bitmaps/ - * - * @param percent Percent of available app memory to use to size memory cache - */ - public void setMemCacheSizePercent(float percent) { - if (percent < 0.01f || percent > 0.8f) { - throw new IllegalArgumentException("setMemCacheSizePercent - percent must be " - + "between 0.01 and 0.8 (inclusive)"); - } - memCacheSize = Math.round(percent * Runtime.getRuntime().maxMemory() / 1024); - } - } - - /** - * @param candidate - Bitmap to check - * @param targetOptions - Options that have the out* value populated - * @return true if candidate can be used for inBitmap re-use with - * targetOptions - */ - @TargetApi(VERSION_CODES.KITKAT) - private static boolean canUseForInBitmap( - Bitmap candidate, BitmapFactory.Options targetOptions) { - - if (!Utils.hasKitKat()) { - // On earlier versions, the dimensions must match exactly and the inSampleSize must be 1 - return candidate.getWidth() == targetOptions.outWidth - && candidate.getHeight() == targetOptions.outHeight - && targetOptions.inSampleSize == 1; - } - - // From Android 4.4 (KitKat) onward we can re-use if the byte size of the new bitmap - // is smaller than the reusable bitmap candidate allocation byte count. - int width = targetOptions.outWidth / targetOptions.inSampleSize; - int height = targetOptions.outHeight / targetOptions.inSampleSize; - int byteCount = width * height * getBytesPerPixel(candidate.getConfig()); - return byteCount <= candidate.getAllocationByteCount(); - } - - /** - * Return the byte usage per pixel of a bitmap based on its configuration. - * @param config The bitmap configuration. - * @return The byte usage per pixel. - */ - private static int getBytesPerPixel(Config config) { - if (config == Config.ARGB_8888) { - return 4; - } else if (config == Config.RGB_565) { - return 2; - } else if (config == Config.ARGB_4444) { - return 2; - } else if (config == Config.ALPHA_8) { - return 1; - } - return 1; - } - - /** - * Get a usable cache directory (external if available, internal otherwise). - * - * @param context The context to use - * @param uniqueName A unique directory name to append to the cache dir - * @return The cache dir - */ - public static File getDiskCacheDir(Context context, String uniqueName) { - // Check if media is mounted or storage is built-in, if so, try and use external cache dir - // otherwise use internal cache dir - final String cachePath = - Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) || - !isExternalStorageRemovable() ? getExternalCacheDir(context).getPath() : - context.getCacheDir().getPath(); - - return new File(cachePath + File.separator + uniqueName); - } - - /** - * A hashing method that changes a string (like a URL) into a hash suitable for using as a - * disk filename. - */ - public static String hashKeyForDisk(String key) { - String cacheKey; - try { - final MessageDigest mDigest = MessageDigest.getInstance("MD5"); - mDigest.update(key.getBytes()); - cacheKey = bytesToHexString(mDigest.digest()); - } catch (NoSuchAlgorithmException e) { - cacheKey = String.valueOf(key.hashCode()); - } - return cacheKey; - } - - private static String bytesToHexString(byte[] bytes) { - // http://stackoverflow.com/questions/332079 - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < bytes.length; i++) { - String hex = Integer.toHexString(0xFF & bytes[i]); - if (hex.length() == 1) { - sb.append('0'); - } - sb.append(hex); - } - return sb.toString(); - } - - /** - * Get the size in bytes of a bitmap in a BitmapDrawable. Note that from Android 4.4 (KitKat) - * onward this returns the allocated memory size of the bitmap which can be larger than the - * actual bitmap data byte count (in the case it was re-used). - * - * @param value - * @return size in bytes - */ - @TargetApi(VERSION_CODES.KITKAT) - public static int getBitmapSize(BitmapDrawable value) { - Bitmap bitmap = value.getBitmap(); - - // From KitKat onward use getAllocationByteCount() as allocated bytes can potentially be - // larger than bitmap byte count. - if (Utils.hasKitKat()) { - return bitmap.getAllocationByteCount(); - } - - if (Utils.hasHoneycombMR1()) { - return bitmap.getByteCount(); - } - - // Pre HC-MR1 - return bitmap.getRowBytes() * bitmap.getHeight(); - } - - /** - * Check if external storage is built-in or removable. - * - * @return True if external storage is removable (like an SD card), false - * otherwise. - */ - @TargetApi(VERSION_CODES.GINGERBREAD) - public static boolean isExternalStorageRemovable() { - if (Utils.hasGingerbread()) { - return Environment.isExternalStorageRemovable(); - } - return true; - } - - /** - * Get the external app cache directory. - * - * @param context The context to use - * @return The external cache dir - */ - @TargetApi(VERSION_CODES.FROYO) - public static File getExternalCacheDir(Context context) { - if (Utils.hasFroyo()) { - return context.getExternalCacheDir(); - } - - // Before Froyo we need to construct the external cache dir ourselves - final String cacheDir = "/Android/data/" + context.getPackageName() + "/cache/"; - return new File(Environment.getExternalStorageDirectory().getPath() + cacheDir); - } - - /** - * Check how much usable space is available at a given path. - * - * @param path The path to check - * @return The space available in bytes - */ - @TargetApi(VERSION_CODES.GINGERBREAD) - public static long getUsableSpace(File path) { - if (Utils.hasGingerbread()) { - return path.getUsableSpace(); - } - final StatFs stats = new StatFs(path.getPath()); - return (long) stats.getBlockSize() * (long) stats.getAvailableBlocks(); - } - - /** - * Locate an existing instance of this Fragment or if not found, create and - * add it using FragmentManager. - * - * @param fm The FragmentManager manager to use. - * @return The existing instance of the Fragment or the new instance if just - * created. - */ - private static RetainFragment findOrCreateRetainFragment(FragmentManager fm) { - // Check to see if we have retained the worker fragment. - RetainFragment mRetainFragment = (RetainFragment) fm.findFragmentByTag(TAG); - - // If not retained (or first time running), we need to create and add it. - if (mRetainFragment == null) { - mRetainFragment = new RetainFragment(); - fm.beginTransaction().add(mRetainFragment, TAG).commitAllowingStateLoss(); - } - - return mRetainFragment; - } - - /** - * A simple non-UI Fragment that stores a single Object and is retained over configuration - * changes. It will be used to retain the ImageCache object. - */ - public static class RetainFragment extends Fragment { - private Object mObject; - - /** - * Empty constructor as per the Fragment documentation - */ - public RetainFragment() {} - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - // Make sure this Fragment is retained over a configuration change - setRetainInstance(true); - } - - /** - * Store a single object in this Fragment. - * - * @param object The object to store - */ - public void setObject(Object object) { - mObject = object; - } - - /** - * Get the stored object. - * - * @return The stored object - */ - public Object getObject() { - return mObject; - } - } - -} diff --git a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/ImageFetcher.java b/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/ImageFetcher.java deleted file mode 100644 index 4c92d7417..000000000 --- a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/ImageFetcher.java +++ /dev/null @@ -1,311 +0,0 @@ -/* - * Copyright (C) 2012 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.example.android.bitmapfun.util; - -import android.content.Context; -import android.graphics.Bitmap; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; -import android.os.Build; -import android.util.Log; -import android.widget.Toast; - -import com.example.android.bitmapfun.BuildConfig; -import com.example.android.bitmapfun.R; - -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileDescriptor; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.net.HttpURLConnection; -import java.net.URL; - -/** - * A simple subclass of {@link ImageResizer} that fetches and resizes images fetched from a URL. - */ -public class ImageFetcher extends ImageResizer { - private static final String TAG = "ImageFetcher"; - private static final int HTTP_CACHE_SIZE = 10 * 1024 * 1024; // 10MB - private static final String HTTP_CACHE_DIR = "http"; - private static final int IO_BUFFER_SIZE = 8 * 1024; - - private DiskLruCache mHttpDiskCache; - private File mHttpCacheDir; - private boolean mHttpDiskCacheStarting = true; - private final Object mHttpDiskCacheLock = new Object(); - private static final int DISK_CACHE_INDEX = 0; - - /** - * Initialize providing a target image width and height for the processing images. - * - * @param context - * @param imageWidth - * @param imageHeight - */ - public ImageFetcher(Context context, int imageWidth, int imageHeight) { - super(context, imageWidth, imageHeight); - init(context); - } - - /** - * Initialize providing a single target image size (used for both width and height); - * - * @param context - * @param imageSize - */ - public ImageFetcher(Context context, int imageSize) { - super(context, imageSize); - init(context); - } - - private void init(Context context) { - checkConnection(context); - mHttpCacheDir = ImageCache.getDiskCacheDir(context, HTTP_CACHE_DIR); - } - - @Override - protected void initDiskCacheInternal() { - super.initDiskCacheInternal(); - initHttpDiskCache(); - } - - private void initHttpDiskCache() { - if (!mHttpCacheDir.exists()) { - mHttpCacheDir.mkdirs(); - } - synchronized (mHttpDiskCacheLock) { - if (ImageCache.getUsableSpace(mHttpCacheDir) > HTTP_CACHE_SIZE) { - try { - mHttpDiskCache = DiskLruCache.open(mHttpCacheDir, 1, 1, HTTP_CACHE_SIZE); - if (BuildConfig.DEBUG) { - Log.d(TAG, "HTTP cache initialized"); - } - } catch (IOException e) { - mHttpDiskCache = null; - } - } - mHttpDiskCacheStarting = false; - mHttpDiskCacheLock.notifyAll(); - } - } - - @Override - protected void clearCacheInternal() { - super.clearCacheInternal(); - synchronized (mHttpDiskCacheLock) { - if (mHttpDiskCache != null && !mHttpDiskCache.isClosed()) { - try { - mHttpDiskCache.delete(); - if (BuildConfig.DEBUG) { - Log.d(TAG, "HTTP cache cleared"); - } - } catch (IOException e) { - Log.e(TAG, "clearCacheInternal - " + e); - } - mHttpDiskCache = null; - mHttpDiskCacheStarting = true; - initHttpDiskCache(); - } - } - } - - @Override - protected void flushCacheInternal() { - super.flushCacheInternal(); - synchronized (mHttpDiskCacheLock) { - if (mHttpDiskCache != null) { - try { - mHttpDiskCache.flush(); - if (BuildConfig.DEBUG) { - Log.d(TAG, "HTTP cache flushed"); - } - } catch (IOException e) { - Log.e(TAG, "flush - " + e); - } - } - } - } - - @Override - protected void closeCacheInternal() { - super.closeCacheInternal(); - synchronized (mHttpDiskCacheLock) { - if (mHttpDiskCache != null) { - try { - if (!mHttpDiskCache.isClosed()) { - mHttpDiskCache.close(); - mHttpDiskCache = null; - if (BuildConfig.DEBUG) { - Log.d(TAG, "HTTP cache closed"); - } - } - } catch (IOException e) { - Log.e(TAG, "closeCacheInternal - " + e); - } - } - } - } - - /** - * Simple network connection check. - * - * @param context - */ - private void checkConnection(Context context) { - final ConnectivityManager cm = - (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); - final NetworkInfo networkInfo = cm.getActiveNetworkInfo(); - if (networkInfo == null || !networkInfo.isConnectedOrConnecting()) { - Toast.makeText(context, R.string.no_network_connection_toast, Toast.LENGTH_LONG).show(); - Log.e(TAG, "checkConnection - no connection found"); - } - } - - /** - * The main process method, which will be called by the ImageWorker in the AsyncTask background - * thread. - * - * @param data The data to load the bitmap, in this case, a regular http URL - * @return The downloaded and resized bitmap - */ - private Bitmap processBitmap(String data) { - if (BuildConfig.DEBUG) { - Log.d(TAG, "processBitmap - " + data); - } - - final String key = ImageCache.hashKeyForDisk(data); - FileDescriptor fileDescriptor = null; - FileInputStream fileInputStream = null; - DiskLruCache.Snapshot snapshot; - synchronized (mHttpDiskCacheLock) { - // Wait for disk cache to initialize - while (mHttpDiskCacheStarting) { - try { - mHttpDiskCacheLock.wait(); - } catch (InterruptedException e) {} - } - - if (mHttpDiskCache != null) { - try { - snapshot = mHttpDiskCache.get(key); - if (snapshot == null) { - if (BuildConfig.DEBUG) { - Log.d(TAG, "processBitmap, not found in http cache, downloading..."); - } - DiskLruCache.Editor editor = mHttpDiskCache.edit(key); - if (editor != null) { - if (downloadUrlToStream(data, - editor.newOutputStream(DISK_CACHE_INDEX))) { - editor.commit(); - } else { - editor.abort(); - } - } - snapshot = mHttpDiskCache.get(key); - } - if (snapshot != null) { - fileInputStream = - (FileInputStream) snapshot.getInputStream(DISK_CACHE_INDEX); - fileDescriptor = fileInputStream.getFD(); - } - } catch (IOException e) { - Log.e(TAG, "processBitmap - " + e); - } catch (IllegalStateException e) { - Log.e(TAG, "processBitmap - " + e); - } finally { - if (fileDescriptor == null && fileInputStream != null) { - try { - fileInputStream.close(); - } catch (IOException e) {} - } - } - } - } - - Bitmap bitmap = null; - if (fileDescriptor != null) { - bitmap = decodeSampledBitmapFromDescriptor(fileDescriptor, mImageWidth, - mImageHeight, getImageCache()); - } - if (fileInputStream != null) { - try { - fileInputStream.close(); - } catch (IOException e) {} - } - return bitmap; - } - - @Override - protected Bitmap processBitmap(Object data) { - return processBitmap(String.valueOf(data)); - } - - /** - * Download a bitmap from a URL and write the content to an output stream. - * - * @param urlString The URL to fetch - * @return true if successful, false otherwise - */ - public boolean downloadUrlToStream(String urlString, OutputStream outputStream) { - disableConnectionReuseIfNecessary(); - HttpURLConnection urlConnection = null; - BufferedOutputStream out = null; - BufferedInputStream in = null; - - try { - final URL url = new URL(urlString); - urlConnection = (HttpURLConnection) url.openConnection(); - in = new BufferedInputStream(urlConnection.getInputStream(), IO_BUFFER_SIZE); - out = new BufferedOutputStream(outputStream, IO_BUFFER_SIZE); - - int b; - while ((b = in.read()) != -1) { - out.write(b); - } - return true; - } catch (final IOException e) { - Log.e(TAG, "Error in downloadBitmap - " + e); - } finally { - if (urlConnection != null) { - urlConnection.disconnect(); - } - try { - if (out != null) { - out.close(); - } - if (in != null) { - in.close(); - } - } catch (final IOException e) {} - } - return false; - } - - /** - * Workaround for bug pre-Froyo, see here for more info: - * http://android-developers.blogspot.com/2011/09/androids-http-clients.html - */ - public static void disableConnectionReuseIfNecessary() { - // HTTP connection reuse which was buggy pre-froyo - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.FROYO) { - System.setProperty("http.keepAlive", "false"); - } - } -} diff --git a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/ImageResizer.java b/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/ImageResizer.java deleted file mode 100644 index e8ce4e12e..000000000 --- a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/ImageResizer.java +++ /dev/null @@ -1,264 +0,0 @@ -/* - * Copyright (C) 2012 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.example.android.bitmapfun.util; - -import android.annotation.TargetApi; -import android.content.Context; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.os.Build; -import android.util.Log; - -import com.example.android.bitmapfun.BuildConfig; - -import java.io.FileDescriptor; - -/** - * A simple subclass of {@link ImageWorker} that resizes images from resources given a target width - * and height. Useful for when the input images might be too large to simply load directly into - * memory. - */ -public class ImageResizer extends ImageWorker { - private static final String TAG = "ImageResizer"; - protected int mImageWidth; - protected int mImageHeight; - - /** - * Initialize providing a single target image size (used for both width and height); - * - * @param context - * @param imageWidth - * @param imageHeight - */ - public ImageResizer(Context context, int imageWidth, int imageHeight) { - super(context); - setImageSize(imageWidth, imageHeight); - } - - /** - * Initialize providing a single target image size (used for both width and height); - * - * @param context - * @param imageSize - */ - public ImageResizer(Context context, int imageSize) { - super(context); - setImageSize(imageSize); - } - - /** - * Set the target image width and height. - * - * @param width - * @param height - */ - public void setImageSize(int width, int height) { - mImageWidth = width; - mImageHeight = height; - } - - /** - * Set the target image size (width and height will be the same). - * - * @param size - */ - public void setImageSize(int size) { - setImageSize(size, size); - } - - /** - * The main processing method. This happens in a background task. In this case we are just - * sampling down the bitmap and returning it from a resource. - * - * @param resId - * @return - */ - private Bitmap processBitmap(int resId) { - if (BuildConfig.DEBUG) { - Log.d(TAG, "processBitmap - " + resId); - } - return decodeSampledBitmapFromResource(mResources, resId, mImageWidth, - mImageHeight, getImageCache()); - } - - @Override - protected Bitmap processBitmap(Object data) { - return processBitmap(Integer.parseInt(String.valueOf(data))); - } - - /** - * Decode and sample down a bitmap from resources to the requested width and height. - * - * @param res The resources object containing the image data - * @param resId The resource id of the image data - * @param reqWidth The requested width of the resulting bitmap - * @param reqHeight The requested height of the resulting bitmap - * @param cache The ImageCache used to find candidate bitmaps for use with inBitmap - * @return A bitmap sampled down from the original with the same aspect ratio and dimensions - * that are equal to or greater than the requested width and height - */ - public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, - int reqWidth, int reqHeight, ImageCache cache) { - - // First decode with inJustDecodeBounds=true to check dimensions - final BitmapFactory.Options options = new BitmapFactory.Options(); - options.inJustDecodeBounds = true; - BitmapFactory.decodeResource(res, resId, options); - - // Calculate inSampleSize - options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); - - // If we're running on Honeycomb or newer, try to use inBitmap - if (Utils.hasHoneycomb()) { - addInBitmapOptions(options, cache); - } - - // Decode bitmap with inSampleSize set - options.inJustDecodeBounds = false; - return BitmapFactory.decodeResource(res, resId, options); - } - - /** - * Decode and sample down a bitmap from a file to the requested width and height. - * - * @param filename The full path of the file to decode - * @param reqWidth The requested width of the resulting bitmap - * @param reqHeight The requested height of the resulting bitmap - * @param cache The ImageCache used to find candidate bitmaps for use with inBitmap - * @return A bitmap sampled down from the original with the same aspect ratio and dimensions - * that are equal to or greater than the requested width and height - */ - public static Bitmap decodeSampledBitmapFromFile(String filename, - int reqWidth, int reqHeight, ImageCache cache) { - - // First decode with inJustDecodeBounds=true to check dimensions - final BitmapFactory.Options options = new BitmapFactory.Options(); - options.inJustDecodeBounds = true; - BitmapFactory.decodeFile(filename, options); - - // Calculate inSampleSize - options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); - - // If we're running on Honeycomb or newer, try to use inBitmap - if (Utils.hasHoneycomb()) { - addInBitmapOptions(options, cache); - } - - // Decode bitmap with inSampleSize set - options.inJustDecodeBounds = false; - return BitmapFactory.decodeFile(filename, options); - } - - /** - * Decode and sample down a bitmap from a file input stream to the requested width and height. - * - * @param fileDescriptor The file descriptor to read from - * @param reqWidth The requested width of the resulting bitmap - * @param reqHeight The requested height of the resulting bitmap - * @param cache The ImageCache used to find candidate bitmaps for use with inBitmap - * @return A bitmap sampled down from the original with the same aspect ratio and dimensions - * that are equal to or greater than the requested width and height - */ - public static Bitmap decodeSampledBitmapFromDescriptor( - FileDescriptor fileDescriptor, int reqWidth, int reqHeight, ImageCache cache) { - - // First decode with inJustDecodeBounds=true to check dimensions - final BitmapFactory.Options options = new BitmapFactory.Options(); - options.inJustDecodeBounds = true; - BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options); - - // Calculate inSampleSize - options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); - - // Decode bitmap with inSampleSize set - options.inJustDecodeBounds = false; - - // If we're running on Honeycomb or newer, try to use inBitmap - if (Utils.hasHoneycomb()) { - addInBitmapOptions(options, cache); - } - - return BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options); - } - - @TargetApi(Build.VERSION_CODES.HONEYCOMB) - private static void addInBitmapOptions(BitmapFactory.Options options, ImageCache cache) { - // inBitmap only works with mutable bitmaps so force the decoder to - // return mutable bitmaps. - options.inMutable = true; - - if (cache != null) { - // Try and find a bitmap to use for inBitmap - Bitmap inBitmap = cache.getBitmapFromReusableSet(options); - - if (inBitmap != null) { - options.inBitmap = inBitmap; - } - } - } - - /** - * Calculate an inSampleSize for use in a {@link BitmapFactory.Options} object when decoding - * bitmaps using the decode* methods from {@link BitmapFactory}. This implementation calculates - * the closest inSampleSize that is a power of 2 and will result in the final decoded bitmap - * having a width and height equal to or larger than the requested width and height. - * - * @param options An options object with out* params already populated (run through a decode* - * method with inJustDecodeBounds==true - * @param reqWidth The requested width of the resulting bitmap - * @param reqHeight The requested height of the resulting bitmap - * @return The value to be used for inSampleSize - */ - public static int calculateInSampleSize(BitmapFactory.Options options, - int reqWidth, int reqHeight) { - // Raw height and width of image - final int height = options.outHeight; - final int width = options.outWidth; - int inSampleSize = 1; - - if (height > reqHeight || width > reqWidth) { - - final int halfHeight = height / 2; - final int halfWidth = width / 2; - - // Calculate the largest inSampleSize value that is a power of 2 and keeps both - // height and width larger than the requested height and width. - while ((halfHeight / inSampleSize) > reqHeight - && (halfWidth / inSampleSize) > reqWidth) { - inSampleSize *= 2; - } - - // This offers some additional logic in case the image has a strange - // aspect ratio. For example, a panorama may have a much larger - // width than height. In these cases the total pixels might still - // end up being too large to fit comfortably in memory, so we should - // be more aggressive with sample down the image (=larger inSampleSize). - - long totalPixels = width * height / inSampleSize; - - // Anything more than 2x the requested pixels we'll sample down further - final long totalReqPixelsCap = reqWidth * reqHeight * 2; - - while (totalPixels > totalReqPixelsCap) { - inSampleSize *= 2; - totalPixels /= 2; - } - } - return inSampleSize; - } -} diff --git a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/ImageWorker.java b/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/ImageWorker.java deleted file mode 100644 index d26066011..000000000 --- a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/ImageWorker.java +++ /dev/null @@ -1,477 +0,0 @@ -/* - * Copyright (C) 2012 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.example.android.bitmapfun.util; - -import com.example.android.bitmapfun.BuildConfig; - -import android.content.Context; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.ColorDrawable; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.TransitionDrawable; -import android.support.v4.app.FragmentActivity; -import android.support.v4.app.FragmentManager; -import android.util.Log; -import android.widget.ImageView; - -import java.lang.ref.WeakReference; - -/** - * This class wraps up completing some arbitrary long running work when loading a bitmap to an - * ImageView. It handles things like using a memory and disk cache, running the work in a background - * thread and setting a placeholder image. - */ -public abstract class ImageWorker { - private static final String TAG = "ImageWorker"; - private static final int FADE_IN_TIME = 200; - - private ImageCache mImageCache; - private ImageCache.ImageCacheParams mImageCacheParams; - private Bitmap mLoadingBitmap; - private boolean mFadeInBitmap = true; - private boolean mExitTasksEarly = false; - protected boolean mPauseWork = false; - private final Object mPauseWorkLock = new Object(); - - protected Resources mResources; - - private static final int MESSAGE_CLEAR = 0; - private static final int MESSAGE_INIT_DISK_CACHE = 1; - private static final int MESSAGE_FLUSH = 2; - private static final int MESSAGE_CLOSE = 3; - - protected ImageWorker(Context context) { - mResources = context.getResources(); - } - - /** - * Load an image specified by the data parameter into an ImageView (override - * {@link ImageWorker#processBitmap(Object)} to define the processing logic). A memory and - * disk cache will be used if an {@link ImageCache} has been added using - * {@link ImageWorker#addImageCache(FragmentManager, ImageCache.ImageCacheParams)}. If the - * image is found in the memory cache, it is set immediately, otherwise an {@link AsyncTask} - * will be created to asynchronously load the bitmap. - * - * @param data The URL of the image to download. - * @param imageView The ImageView to bind the downloaded image to. - */ - public void loadImage(Object data, ImageView imageView) { - if (data == null) { - return; - } - - BitmapDrawable value = null; - - if (mImageCache != null) { - value = mImageCache.getBitmapFromMemCache(String.valueOf(data)); - } - - if (value != null) { - // Bitmap found in memory cache - imageView.setImageDrawable(value); - } else if (cancelPotentialWork(data, imageView)) { - final BitmapWorkerTask task = new BitmapWorkerTask(imageView); - final AsyncDrawable asyncDrawable = - new AsyncDrawable(mResources, mLoadingBitmap, task); - imageView.setImageDrawable(asyncDrawable); - - // NOTE: This uses a custom version of AsyncTask that has been pulled from the - // framework and slightly modified. Refer to the docs at the top of the class - // for more info on what was changed. - task.executeOnExecutor(AsyncTask.DUAL_THREAD_EXECUTOR, data); - } - } - - /** - * Set placeholder bitmap that shows when the the background thread is running. - * - * @param bitmap - */ - public void setLoadingImage(Bitmap bitmap) { - mLoadingBitmap = bitmap; - } - - /** - * Set placeholder bitmap that shows when the the background thread is running. - * - * @param resId - */ - public void setLoadingImage(int resId) { - mLoadingBitmap = BitmapFactory.decodeResource(mResources, resId); - } - - /** - * Adds an {@link ImageCache} to this {@link ImageWorker} to handle disk and memory bitmap - * caching. - * @param fragmentManager - * @param cacheParams The cache parameters to use for the image cache. - */ - public void addImageCache(FragmentManager fragmentManager, - ImageCache.ImageCacheParams cacheParams) { - mImageCacheParams = cacheParams; - mImageCache = ImageCache.getInstance(fragmentManager, mImageCacheParams); - new CacheAsyncTask().execute(MESSAGE_INIT_DISK_CACHE); - } - - /** - * Adds an {@link ImageCache} to this {@link ImageWorker} to handle disk and memory bitmap - * caching. - * @param activity - * @param diskCacheDirectoryName See - * {@link ImageCache.ImageCacheParams#ImageCacheParams(Context, String)}. - */ - public void addImageCache(FragmentActivity activity, String diskCacheDirectoryName) { - mImageCacheParams = new ImageCache.ImageCacheParams(activity, diskCacheDirectoryName); - mImageCache = ImageCache.getInstance(activity.getSupportFragmentManager(), mImageCacheParams); - new CacheAsyncTask().execute(MESSAGE_INIT_DISK_CACHE); - } - - /** - * If set to true, the image will fade-in once it has been loaded by the background thread. - */ - public void setImageFadeIn(boolean fadeIn) { - mFadeInBitmap = fadeIn; - } - - public void setExitTasksEarly(boolean exitTasksEarly) { - mExitTasksEarly = exitTasksEarly; - setPauseWork(false); - } - - /** - * Subclasses should override this to define any processing or work that must happen to produce - * the final bitmap. This will be executed in a background thread and be long running. For - * example, you could resize a large bitmap here, or pull down an image from the network. - * - * @param data The data to identify which image to process, as provided by - * {@link ImageWorker#loadImage(Object, ImageView)} - * @return The processed bitmap - */ - protected abstract Bitmap processBitmap(Object data); - - /** - * @return The {@link ImageCache} object currently being used by this ImageWorker. - */ - protected ImageCache getImageCache() { - return mImageCache; - } - - /** - * Cancels any pending work attached to the provided ImageView. - * @param imageView - */ - public static void cancelWork(ImageView imageView) { - final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView); - if (bitmapWorkerTask != null) { - bitmapWorkerTask.cancel(true); - if (BuildConfig.DEBUG) { - final Object bitmapData = bitmapWorkerTask.data; - Log.d(TAG, "cancelWork - cancelled work for " + bitmapData); - } - } - } - - /** - * Returns true if the current work has been canceled or if there was no work in - * progress on this image view. - * Returns false if the work in progress deals with the same data. The work is not - * stopped in that case. - */ - public static boolean cancelPotentialWork(Object data, ImageView imageView) { - final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView); - - if (bitmapWorkerTask != null) { - final Object bitmapData = bitmapWorkerTask.data; - if (bitmapData == null || !bitmapData.equals(data)) { - bitmapWorkerTask.cancel(true); - if (BuildConfig.DEBUG) { - Log.d(TAG, "cancelPotentialWork - cancelled work for " + data); - } - } else { - // The same work is already in progress. - return false; - } - } - return true; - } - - /** - * @param imageView Any imageView - * @return Retrieve the currently active work task (if any) associated with this imageView. - * null if there is no such task. - */ - private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) { - if (imageView != null) { - final Drawable drawable = imageView.getDrawable(); - if (drawable instanceof AsyncDrawable) { - final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable; - return asyncDrawable.getBitmapWorkerTask(); - } - } - return null; - } - - /** - * The actual AsyncTask that will asynchronously process the image. - */ - private class BitmapWorkerTask extends AsyncTask { - private Object data; - private final WeakReference imageViewReference; - - public BitmapWorkerTask(ImageView imageView) { - imageViewReference = new WeakReference(imageView); - } - - /** - * Background processing. - */ - @Override - protected BitmapDrawable doInBackground(Object... params) { - if (BuildConfig.DEBUG) { - Log.d(TAG, "doInBackground - starting work"); - } - - data = params[0]; - final String dataString = String.valueOf(data); - Bitmap bitmap = null; - BitmapDrawable drawable = null; - - // Wait here if work is paused and the task is not cancelled - synchronized (mPauseWorkLock) { - while (mPauseWork && !isCancelled()) { - try { - mPauseWorkLock.wait(); - } catch (InterruptedException e) {} - } - } - - // If the image cache is available and this task has not been cancelled by another - // thread and the ImageView that was originally bound to this task is still bound back - // to this task and our "exit early" flag is not set then try and fetch the bitmap from - // the cache - if (mImageCache != null && !isCancelled() && getAttachedImageView() != null - && !mExitTasksEarly) { - bitmap = mImageCache.getBitmapFromDiskCache(dataString); - } - - // If the bitmap was not found in the cache and this task has not been cancelled by - // another thread and the ImageView that was originally bound to this task is still - // bound back to this task and our "exit early" flag is not set, then call the main - // process method (as implemented by a subclass) - if (bitmap == null && !isCancelled() && getAttachedImageView() != null - && !mExitTasksEarly) { - bitmap = processBitmap(params[0]); - } - - // If the bitmap was processed and the image cache is available, then add the processed - // bitmap to the cache for future use. Note we don't check if the task was cancelled - // here, if it was, and the thread is still running, we may as well add the processed - // bitmap to our cache as it might be used again in the future - if (bitmap != null) { - if (Utils.hasHoneycomb()) { - // Running on Honeycomb or newer, so wrap in a standard BitmapDrawable - drawable = new BitmapDrawable(mResources, bitmap); - } else { - // Running on Gingerbread or older, so wrap in a RecyclingBitmapDrawable - // which will recycle automagically - drawable = new RecyclingBitmapDrawable(mResources, bitmap); - } - - if (mImageCache != null) { - mImageCache.addBitmapToCache(dataString, drawable); - } - } - - if (BuildConfig.DEBUG) { - Log.d(TAG, "doInBackground - finished work"); - } - - return drawable; - } - - /** - * Once the image is processed, associates it to the imageView - */ - @Override - protected void onPostExecute(BitmapDrawable value) { - // if cancel was called on this task or the "exit early" flag is set then we're done - if (isCancelled() || mExitTasksEarly) { - value = null; - } - - final ImageView imageView = getAttachedImageView(); - if (value != null && imageView != null) { - if (BuildConfig.DEBUG) { - Log.d(TAG, "onPostExecute - setting bitmap"); - } - setImageDrawable(imageView, value); - } - } - - @Override - protected void onCancelled(BitmapDrawable value) { - super.onCancelled(value); - synchronized (mPauseWorkLock) { - mPauseWorkLock.notifyAll(); - } - } - - /** - * Returns the ImageView associated with this task as long as the ImageView's task still - * points to this task as well. Returns null otherwise. - */ - private ImageView getAttachedImageView() { - final ImageView imageView = imageViewReference.get(); - final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView); - - if (this == bitmapWorkerTask) { - return imageView; - } - - return null; - } - } - - /** - * A custom Drawable that will be attached to the imageView while the work is in progress. - * Contains a reference to the actual worker task, so that it can be stopped if a new binding is - * required, and makes sure that only the last started worker process can bind its result, - * independently of the finish order. - */ - private static class AsyncDrawable extends BitmapDrawable { - private final WeakReference bitmapWorkerTaskReference; - - public AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) { - super(res, bitmap); - bitmapWorkerTaskReference = - new WeakReference(bitmapWorkerTask); - } - - public BitmapWorkerTask getBitmapWorkerTask() { - return bitmapWorkerTaskReference.get(); - } - } - - /** - * Called when the processing is complete and the final drawable should be - * set on the ImageView. - * - * @param imageView - * @param drawable - */ - private void setImageDrawable(ImageView imageView, Drawable drawable) { - if (mFadeInBitmap) { - // Transition drawable with a transparent drawable and the final drawable - final TransitionDrawable td = - new TransitionDrawable(new Drawable[] { - new ColorDrawable(android.R.color.transparent), - drawable - }); - // Set background to loading bitmap - imageView.setBackgroundDrawable( - new BitmapDrawable(mResources, mLoadingBitmap)); - - imageView.setImageDrawable(td); - td.startTransition(FADE_IN_TIME); - } else { - imageView.setImageDrawable(drawable); - } - } - - /** - * Pause any ongoing background work. This can be used as a temporary - * measure to improve performance. For example background work could - * be paused when a ListView or GridView is being scrolled using a - * {@link android.widget.AbsListView.OnScrollListener} to keep - * scrolling smooth. - *

- * If work is paused, be sure setPauseWork(false) is called again - * before your fragment or activity is destroyed (for example during - * {@link android.app.Activity#onPause()}), or there is a risk the - * background thread will never finish. - */ - public void setPauseWork(boolean pauseWork) { - synchronized (mPauseWorkLock) { - mPauseWork = pauseWork; - if (!mPauseWork) { - mPauseWorkLock.notifyAll(); - } - } - } - - protected class CacheAsyncTask extends AsyncTask { - - @Override - protected Void doInBackground(Object... params) { - switch ((Integer)params[0]) { - case MESSAGE_CLEAR: - clearCacheInternal(); - break; - case MESSAGE_INIT_DISK_CACHE: - initDiskCacheInternal(); - break; - case MESSAGE_FLUSH: - flushCacheInternal(); - break; - case MESSAGE_CLOSE: - closeCacheInternal(); - break; - } - return null; - } - } - - protected void initDiskCacheInternal() { - if (mImageCache != null) { - mImageCache.initDiskCache(); - } - } - - protected void clearCacheInternal() { - if (mImageCache != null) { - mImageCache.clearCache(); - } - } - - protected void flushCacheInternal() { - if (mImageCache != null) { - mImageCache.flush(); - } - } - - protected void closeCacheInternal() { - if (mImageCache != null) { - mImageCache.close(); - mImageCache = null; - } - } - - public void clearCache() { - new CacheAsyncTask().execute(MESSAGE_CLEAR); - } - - public void flushCache() { - new CacheAsyncTask().execute(MESSAGE_FLUSH); - } - - public void closeCache() { - new CacheAsyncTask().execute(MESSAGE_CLOSE); - } -} diff --git a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/RecyclingBitmapDrawable.java b/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/RecyclingBitmapDrawable.java deleted file mode 100644 index 2aae97f3c..000000000 --- a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/RecyclingBitmapDrawable.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2013 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.example.android.bitmapfun.util; - -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.drawable.BitmapDrawable; -import android.util.Log; - -import com.example.android.bitmapfun.BuildConfig; - -/** - * A BitmapDrawable that keeps track of whether it is being displayed or cached. - * When the drawable is no longer being displayed or cached, - * {@link Bitmap#recycle() recycle()} will be called on this drawable's bitmap. - */ -public class RecyclingBitmapDrawable extends BitmapDrawable { - - static final String LOG_TAG = "CountingBitmapDrawable"; - - private int mCacheRefCount = 0; - private int mDisplayRefCount = 0; - - private boolean mHasBeenDisplayed; - - public RecyclingBitmapDrawable(Resources res, Bitmap bitmap) { - super(res, bitmap); - } - - /** - * Notify the drawable that the displayed state has changed. Internally a - * count is kept so that the drawable knows when it is no longer being - * displayed. - * - * @param isDisplayed - Whether the drawable is being displayed or not - */ - public void setIsDisplayed(boolean isDisplayed) { - synchronized (this) { - if (isDisplayed) { - mDisplayRefCount++; - mHasBeenDisplayed = true; - } else { - mDisplayRefCount--; - } - } - - // Check to see if recycle() can be called - checkState(); - } - - /** - * Notify the drawable that the cache state has changed. Internally a count - * is kept so that the drawable knows when it is no longer being cached. - * - * @param isCached - Whether the drawable is being cached or not - */ - public void setIsCached(boolean isCached) { - synchronized (this) { - if (isCached) { - mCacheRefCount++; - } else { - mCacheRefCount--; - } - } - - // Check to see if recycle() can be called - checkState(); - } - - private synchronized void checkState() { - // If the drawable cache and display ref counts = 0, and this drawable - // has been displayed, then recycle - if (mCacheRefCount <= 0 && mDisplayRefCount <= 0 && mHasBeenDisplayed - && hasValidBitmap()) { - if (BuildConfig.DEBUG) { - Log.d(LOG_TAG, "No longer being used or cached so recycling. " - + toString()); - } - - getBitmap().recycle(); - } - } - - private synchronized boolean hasValidBitmap() { - Bitmap bitmap = getBitmap(); - return bitmap != null && !bitmap.isRecycled(); - } - -} diff --git a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/Utils.java b/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/Utils.java deleted file mode 100644 index 31dc3423f..000000000 --- a/samples/training/bitmapfun/BitmapFun/src/main/java/com/example/android/bitmapfun/util/Utils.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2012 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.example.android.bitmapfun.util; - -import android.annotation.TargetApi; -import android.os.Build; -import android.os.Build.VERSION_CODES; -import android.os.StrictMode; - -import com.example.android.bitmapfun.ui.ImageDetailActivity; -import com.example.android.bitmapfun.ui.ImageGridActivity; - -/** - * Class containing some static utility methods. - */ -public class Utils { - private Utils() {}; - - - @TargetApi(VERSION_CODES.HONEYCOMB) - public static void enableStrictMode() { - if (Utils.hasGingerbread()) { - StrictMode.ThreadPolicy.Builder threadPolicyBuilder = - new StrictMode.ThreadPolicy.Builder() - .detectAll() - .penaltyLog(); - StrictMode.VmPolicy.Builder vmPolicyBuilder = - new StrictMode.VmPolicy.Builder() - .detectAll() - .penaltyLog(); - - if (Utils.hasHoneycomb()) { - threadPolicyBuilder.penaltyFlashScreen(); - vmPolicyBuilder - .setClassInstanceLimit(ImageGridActivity.class, 1) - .setClassInstanceLimit(ImageDetailActivity.class, 1); - } - StrictMode.setThreadPolicy(threadPolicyBuilder.build()); - StrictMode.setVmPolicy(vmPolicyBuilder.build()); - } - } - - public static boolean hasFroyo() { - // Can use static final constants like FROYO, declared in later versions - // of the OS since they are inlined at compile time. This is guaranteed behavior. - return Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO; - } - - public static boolean hasGingerbread() { - return Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD; - } - - public static boolean hasHoneycomb() { - return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB; - } - - public static boolean hasHoneycombMR1() { - return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1; - } - - public static boolean hasJellyBean() { - return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN; - } - - public static boolean hasKitKat() { - return Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; - } -} diff --git a/samples/training/bitmapfun/BitmapFun/src/main/res/drawable-hdpi/ic_launcher.png b/samples/training/bitmapfun/BitmapFun/src/main/res/drawable-hdpi/ic_launcher.png deleted file mode 100644 index 75b3c978107062bdb3549adb3f59502fe207b5f9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4440 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 diff --git a/samples/training/bitmapfun/BitmapFun/src/main/res/drawable-mdpi/ic_launcher.png b/samples/training/bitmapfun/BitmapFun/src/main/res/drawable-mdpi/ic_launcher.png deleted file mode 100644 index 0c9c11af9ae8289f2a970f8543a85931e2d6affa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2741 zcmV;m3QF~fP)fc zr~fEO0yIq(vXgrde#DUTqErZzMvFsuR!0`gvVx8HZ}J@=knm)!-^mRvpDd(VA+ zzwx=5&At^t=3~I1DMU` z(H2ANZ=X+IzaQls_kcjs_z38UVfDW(dv>I^3MrxsBcdZtk#=`pWKkl`l#dDq9 zuhU4^@$pACivT@z8TVtf$7^Hwhx(0pZo*>|zJdDKFUxZxc-l+0Zx3ysGFfTsvS*KkkZL`EA)%r}{c z1Td2zc*%^{O!$WYOxQ;-M(m*DG3+M*x}SguWK4!Mq%9}Y>B45jX0{!xfas5|cX0sH z1c9(7LlJ~@u4;*C3(rY*f}p+lVnU%&CgEvMLK1{Hx@@$W@T#!Vv2|a|_633Xx^87` zY;+wkmjDETtsyj!6geFh1b8z+Nk}d%1^_iC+X3JTAsBQ*G#3d<+FPKZdkD_7_NfBf z5uSjWCkx;%YvPRsA&~gwbm$s3DYh<-wxi4_7!?zoTUvmp4$9m5;N6A}rU4@fKUsjM zxCX}2!W#=i;OUt@kb0p-a&l((k_jn(z%+pWa4$KpZ1>!Bm_w?LnZHU8SGoq_-D?uS zc*2u8-G14L2|X@=tuNTM{izJVBGLa-1LHZd=mSp&`M_&vLM%tf5r~-(3#mLBso_D= z0)o|_X7 zp)5J!wPP~CA@TtpHYe-E&hkLU!cbU>0Iz;70kA~(n0cUZwI1qF1Bb4)KYkvV^T!4` z57Gc) zfGE5a?7dEK&^oiBm7})xF5WCy%V}eXwqc8QMq6X z!6VU!-;aX2djoKwp8J3rp~U)#pbR^l06h7JYhAox<602U-&q_6@gcsj#*9U^O%0Ch>REoUN$(GH{k`BiR-M29@*doLFr;XqbbU0`pb2Hy-BGl{ za|HJVuC30UP5?rSuXd3$T4!0gSVIkDFA0NZL#DwGKl>WqLUYyT7Msp5aMaT;w!$ZgbD;_WepyQzP%Bpwm_3C2B zCNz@(@IzvT70cuUOT%GB=rl+?{cp%b`+sT96RyZ=_a2(W?7(n*I_W8>>gb2JYYmET zFq07+U7CVg;6aKLfPfF`nF)y@QmplV{Y*HlM1b|D8et#$L-Jz+ynOZ+e6}G5DiPqV z8iN|0$iW2&9<_R%0rVB!%fNiBZ70P%l*-|h zZtENxgHx?NP_Z!V=>Qx_2Pe34rf00#H{MczM8q z^5P?43z|*)AgyZ1n=GuO`LF_`QYnvz`nbdKTMyv%sy1ci!n*8G9eZfk62}_wE~sNZ zV5_z6QuFIEZLAbj%@a!E6PUWo9G&NR`T<R9DrhBM16hzQN3QjcD#>Ih5qK|=Fj8e;?hu-Hf+Nj zfR@#R)6>)A=gph<9ajX!(b5bK4to3h`=@w%dItCQ_EuwGxx2eNn+yyL4EO{E1?|P9 zaOm#tKIrS~%SuzD(ddTD_|N(I`7OieJNoTO%^T?4SRd`b; zwox&75Ck<>f8_AW$jC@rxpL*NQ23sG`}S>YYHDgE8D$A(ojiH+OB5KAlas%TeRt%3 z_vq;8nEm_rm!aS@E?v5mnU|NBPowF|z>(RI;Br%bVsi0W<|7 z@`G7fS$TL>aV|WLtgfzZ866#kgoFe=p0~Bmu5f{*d_y@+qG-g(v*~x3m8ez2?%f|@BpJ!c$$6ooqT(`st>4ZUodM7zzL3R9c38J+7)?q5y%bR;~Ioj@G<}hK3}JFcdb& z3x8oax>x%8`k*iWM4NJKKwq7s$K6yxt<8*)i{ylJ8UtR#}3qmMa?+x(|nl7;;w v>Od$q4VMRl-s@6UruR)0z}12IpWXK#?}GrEnpkT^00000NkvXXu0mjfx!5yi diff --git a/samples/training/bitmapfun/BitmapFun/src/main/res/drawable-nodpi/empty_photo.png b/samples/training/bitmapfun/BitmapFun/src/main/res/drawable-nodpi/empty_photo.png deleted file mode 100644 index da1478a510db0e72a5fa715cf071cea7f100fb95..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3159 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 AZ2$lO diff --git a/samples/training/bitmapfun/BitmapFun/src/main/res/drawable-xhdpi/ic_launcher.png b/samples/training/bitmapfun/BitmapFun/src/main/res/drawable-xhdpi/ic_launcher.png deleted file mode 100644 index 7c5aeed04f594df10128e79bc9d72d3d948cdfa3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6245 zcmV-r7@FsaP)^BPf~(5+Fno zqGE8%aaTNvJWvCOhzbHC$_&U3!@dqM^X^wwxBGTY_dowXJ)36QQ~teFbyt1g-Rf3P zlcwsn8bu(jMxS2ya8*Y@4GTdX0d)k_FakCF_&N=!jRbtT=&LblX&F@1DK*@`AH?HJ zOV#eu(i0Q9_BnfkJD@Te@YSldx94OxobiJgc`X~n!Gi~foz?Y>0Jbgx^>*you{}LK zy-6+W`wyZ!B_$<$TC`}FWcKskmTT2I#o15E|y0$xtfVP`AZ{Ao- z`~HLIR+~0$+R;EZMG=sl&IK(*|^S&yjV>&78X5fI@p%(V3x_=*iVD5wKx*Z;9B0#~Y=aXeC58yy3$y8S z7Hj}{qSVyUpaNtKzaayAh7hzjq~VxJ!1HlmLzEeSByH2ZS6qo(+p?2>3#shkF1y3Z zqzl

RT)_^QJG@;z>KSKm% z9%i$e;3Do_!39&-5Ceh?*pOV7d=G3}&xj|a9XJ`H%1C|yXae1mZ^$r$fM@V|tu1tJ z<6W1urrRC2T`Z>G!}eJBGfbQ1@alDVdQq`f1pzh#FU^nvmBBVZqa6e6Nj@Z9gKH(+ z?gMP85e&X=&y6()WhLJ0uuu~Xh8-f4pru=v5-Y6+bR)S;0N1f*MUw%YFy_eoXGk)p z%lztVf(THFIvZ~917Y^UP6=*O1mr*aX|a}KZSSogfRw`g030o`40cfB(MD?sOpVqU zBmG7rjP1hz`Peg8`dMtgH0rvZY2y%LN2G1DuIU4>CEJ=2V=7#;Vjz%aMuY8{Cj!=$ zd}tNGx5VjvL4+pC`V{Q1zvcXR1f2T!<(dGNb`SKV&KVF*8}2=5s!{s3?Wfb&-k8gi2%$)>j?0)fgQPw zh=Iz|LH=4mSnQ9`6q}5GWKM{4Kum%TL&Y4xfbEB%?2Ju_ z$|+@~9`5DO18U7oI1T|}xgaJ$WLuE1!1pF^dkdvUe0e*NYQ_@~&D$I{GMUV8tM9j>D znSdJ4hE~RU00{V#&4#W70gKrO2+*}DDw}qw&h>x^7y1N@Ui6*%eCOem)6s5HF7|pr zG6b+453G`jgk5)%s>l17To4n$i8|?krCIXV2MkG3_TQTe*o;~r_YbYGxgfb1!?vHL zpRYYa1az|+00K-lRQL(UX(y8@tuz7xAV*|J8NgunG)+MRJOXwd)`>26g-jA60e2T= zl39>Rv=}23aB>9<`_KSMz_J}gK)_M^t7(bW2c&b4O+ZIplH_P5g;g;o4FpW=gIA46 z11teyp=Tlzv;(%{&TGR#Cu~%JQ_E#i6xQy`q@Ak?j|u`tE&NU`(+e^cUk!uytdF}S zng2Z2gylMJ*=5Oa$@iBq&uG; zbjLyarks77B0|#HPlU~`D}{h42h4Un(OVN*16njxlLY~=uTegLL2uKro*H^~b2az7 zed<%P#gYtH`sJ~fc6);Dy?hfQ$_JngLS-i4o_}x9KEREFT7;8Ti%q!W&E$dVM7pp9_}Xg~W4@5QVz`!BU1N+aNe zr(Be$2eyKIz{r2Y1o7G0vsnSB|c30-Ox5R0qvN8B(xw*`n3-r0;UQApx$2E!TObp3G_QV zw@|s+^&$cO*QR~y$<;g65qh&CYzt_Cwf(3kBOyL^!N)NI1&@G_HmIfK17rr(-8Yey zP3AfKOz@_`F-;H%kdp6eK@{kG9={im37UGb)qtYITD>{CX;3ds-O;Iq`dNo2f!9)^ zSXfT&ANhQ%+JEeXS0y|YH4=_Znjje+xw#<(3J4gm-9-ez2`3`JHA$}YqwVIn1hjpQ z2;g~uqXo&FM;-WpzgPm67adY}!vxX2nxB-Z9;V69nT=11l=`p>YxW#b<38J>ijGJU z4B8BOCfT^&xeZnGtW5RB`n{?cFWp*`6CRe9s>!6KOu(HVS^}&F`DqJZ;#taFO_1le z#3i5&5#YYBBV&EfnObrJ;ia85gMjJ%j0S*!|0d~z6F;YEwwh*%Nr?ffyzyYM)`V~O z9`&RgJB{mSsDW)yR#&!ftlH-w08wfky$CjIO_5r-^?;YQmU94@5s>odzC-|-OKf+3 zxL$n%Cu~hjm@}3VA6V?sq#4t<^_K4OqM+6wfNmJ(Y{SftO9+7ONrRf+uY*Sbqzm^$ z-ld`h;ASvGe^|Pi5`0G#!q%(FjQzN6BYGLwyxrz*A8gsLrmfnkwh(!2mZUSDa#4G& zt=NVW0ZX?BO%MYfN`{ytNNtHtK&xjz*HMsX(?u?bLs6F;*JoVNvY~p?67U6SfF^(j ziQGGDMyeXX^$#8^p?ShVbs;??DjMI73R=4D>Kg5bIbkLUzuV$!K(0f8_u3%coK2BSjyuVM6MpJPMgu^=a2g!Q>qc*( z{nZjvQ35L3?kn;m(^l?KOZ05lm>~EmNx%})01r$n;ZY_jN-MViGNuV?`3y9`pM;g2 z!8!}-RzqaHo+O>=M$2d%^@gY0p;_$T;m_r~kZiyPExVkp>#t2w}l?`vUj? zn4q%KE+sr{p|pipTI5R&iu05>uUBW7n@0-A#W>Rfq12a{_-jp;=iFtdNY zM*s!~lDd-#dfJ|k17e)EFA@P0D4GIz@KexMY`4uf1aOF%2>e9Xif%nGUK9iZI1$FC zNKUd`t&rDP6F|VMOAG->@nUcw6thkyCJu;k+P**pJo1?#0NTg|+)91?SZjd%n=;?& zhDp0LJ$!vvTmqW@?K3Y5O5|+iHJ30D@Qfe;gF_u;9{*;~#{n@;TM+QDCE#g6z%N0- zwgcWw*i7E|Z;kDrutxX?>9}{_7E=R2fCf}JR~hR|(`4%TzJiZ?=jH?mxaI!}^!=a# zOiUaQbX4L=<+6ZC6HkkVO@ir3$NkGGn-7r;Z&7QafG?@hr;LN32j`SB# zj8hH+U6OPmr39Y4w4)vva_ocWfdHjCpOUQ}rFU_XN&?|INgxe?lG>;u>b|{UulmcX z9eN+y$OH_s1W0gG1t0bIRt5nKa(S8%b{&%5o1|*?080Q(?{B8T@sx?lL_G$WpT|IY z=TW(w4bq2A62UnjKF<=$c_YrPf&fhDu-!~TvY1SeF+JuzQrS-WfJE2#4T<@O+Kpx| z^L;=Pn!qGTKL8W-yd?k`Mhp&sV{?hXIZ1{-wxB10e;@_OFAlO zE4G`ckBc~WR6+3Z-2Ih7fCkD+sFYY~ju1NBWXf%^7&A9vE^lcstI3X<4vsXT#f$v632P=aBqXCIYNK~f}t#-X=2}lG^ZMt^Dzn}r+1FW-_z-s9R zbyhPH)j1TGfCkV2i%cRC*xa~I73cdB0Wba7Xu$VnyPgQ}*AI-AxIl-UGvA!>{Y#|~ zpru=oV5Hp-L0!k|l7K~$uC(~Vn|Iwm6L{$=U~0rc$|gwI8xA4yNE7||(USBLeC12Z z+U})Pca)S%@Vzx>2Yj4AGPFm$RK!U$%=dz#NHAm=Bm=LK({Y$Ka3mv1L)-Eku&k3v zi-O^2v?FGq8DJ)ci3$T=OSX+&JxOq+2!a^!-3a%M=9gy$a}7E=SVrZ3j1h%f*Xfi1Pcw3SjGKIxKS zsSA2V2#~-V2`~E>G*S~J@UksSbFzLw-lzDk-tkOO{mBb#E{NsJkKd)^#Z~#`L;YPi zvQ1Isg-Las37FqIv&YgOFiDmKS+_hbFrTS(zPM=b@0u%514ZO?Y-Im<1Y|xo*Vh0c z$@oqj6A&ih46r*VNR)Drq_9~EBgzFk>^RI91m*w&L{|&k*q^m0Oha%_d_PWVht!_q z|8qr!2*7M4bK$~;L;KC!HpK;vCEEpw!A1wn-+KlBltNy|K1k$Ek|_aACjAgai1Y!T zbOLzMfWc#}3DX$Q1PRIZrQNOeTwR?=1WI3i`Q=-N3>orIs)3JolY23JRN0=kM;zaiJz<~pW#l^+@6FsVF)28Q8Tl4)+D!X>=T9K%J{rdG!rY&DS zXWzbk1;>vcKTyd-3fzu9*nMEilqvsQw{9K!hwnz?>-65+#V!FjZb79Jl~zKJgp=8njYbPdxF&%mxh_=r%-_8Z~Ov z5UOw9Q!Kkdu(E$kTZaxE^7&asMMaw*dg!6w(mf)-Y+Fe~hwa}=Wi^!@R6JJ`X5X`@ z!pT(d#%%eonpM1qY2(I?8`iB`xBH0)4dT(GM@y$on>Kddym{~04wrFS_HntdzWVBt zoSYop?(pHm>bmQ$!&l$8MRW&()M~(h0Y4cza^wRQAz zMYe<>9ialP4hTt&IoL^)Cgl?NtI7PF0x$sNvSrH_10d?;7gX@2@hA<*`{08Q);DU@ zNP~Fj&>?l{rI&V~`Wqr3&@W?C={9-tN~4g3qpyk5d!ASnR8x#em+b{rZqik z?%cVrjvYI8BK7i1D)_#>J8zN$s0aZVEB<|it`9u$z-R*ezp}EjSc8sl+O(Hz^;w{G1rc<|s6d-v}Bg!+gmp**Kp5dtuL{QXlq zGJ8EK%$`CHxdkF9Xdp#V3$DEK$^i+?UX^fD?F78?$}2a}z+YtP0|B#U&6+x6#*AlZ zgkMof7=Bt5DoOyFBf#i1bm-7qh7TWpcXoERCIDs2mMuRgXZ})^ARTejY9-+D#~;t` z-o5+E=FOXTg#88hUw{4e8j}8BQU5-tf>13g@`-DLxV?^usB|YS_*2)eUC%mkTDEE>;I-FYd!T*$_75WLw9!im+5O+W|Ni@L(`eUHNp|3IGyuS2 zdG3sX0|)-}!3Q6Fh)hyCrUG!tWDfsu?X}mAtrqm@A=O5}Q%^nB?(DPAM%bCdG5F%e zi|3CXJ^CT)$8su|qDO_DE*^0SKv;^D%efOLPW)Z}{{1h(tQrJRNPA%I+O?gA4I5T1 z|DIZP5s>!gn{Uphu=6IQ0wDc+_Ut)$#~pXvTToE&ApucHC6J^lM*`y#fZHSKnBTEu z$9^OnPqb{=vLVbO`nG-h_Bj+K-AvaLMPikrB-KIyWmfxk>C)vxnt`Ts*!lYFufH&1 z!h|U_ij`FG9fDK_TNwmk?2V~(88c?g2+I8af&&B6gc9;i{V0REuu@P?aFgmFAe|zM zMb}(&O|LW0Jd^W1TPQm?g2?!k`i6hj54rMGw!HcC=dY*ans(%v36ni9Zz2|UfBdsu zZ7EULGpF(q!)(A_NE?uhLefQl@-g@h;-YJ;HRQm7Lt#SfrG2%8dJu~5WX`t%j zi!a9D{z&2I3)G7>R4S5<%%fEKdWxoCE;I?0KdJ};00PK#!&x|??L`0S;s4fly~{% zm#?S$s+~J`YOp9bR8a!pkaMYYyWoNgCY*NKY3Cv$qKTQJU}57&AAQuZ0vehm{8b+X zHM-)8D^`;9x7)pYw<0HZoc^)U-;e{IOFjIS3NnNh+43m)24d4GrHtsMWYMBU04h-~ zDA99}`8};klO{ju(WA!%`pu+1B0sfp$&w{q=-R%DbTXMXRjmQJv~Y4&>(;GD5ec1W zP;VC&7EY#)VR0g`GL%ebMV&mFKs`h0j2~H`7OYvb=GGVl00)GnUC9sJOEdMK(n88= zy659W0)(Z?GNn~b00wmqmD8yp8@YuFOb`B5(Nw}tmmmE5j2BYLw-*ZO_hWni6uI=O zBP_*-?YdFH{gJo-MhHtq@0+k)RTBWvvBHLBPJ9JpJC*fRlF9cZ>Ubl%h_`*|@0;|7 zNNOFGxTXSmBKQ*gd&-BXU^0dktf-Z?*rZf70q7t?J%Ep23?$J~W3mH6J_q+SpEfaI zp(K+>O7sl9bV&t*gU}fFOf^JKb_}{+br6tJhyK$l*AY;|jMouR!vy?4x+%|Z_27e? P00000NkvXXu0mjf$Q!N? diff --git a/samples/training/bitmapfun/BitmapFun/src/main/res/drawable-xxhdpi/ic_launcher.png b/samples/training/bitmapfun/BitmapFun/src/main/res/drawable-xxhdpi/ic_launcher.png deleted file mode 100644 index 91b0f960b2811b942d44da4bee5e9dbab36f189c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11128 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 KMNUMnLSTY7Oa - - - - - - - - - - - - - - - - diff --git a/samples/training/bitmapfun/BitmapFun/src/main/res/layout/image_detail_fragment.xml b/samples/training/bitmapfun/BitmapFun/src/main/res/layout/image_detail_fragment.xml deleted file mode 100644 index 6940357af..000000000 --- a/samples/training/bitmapfun/BitmapFun/src/main/res/layout/image_detail_fragment.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/samples/training/bitmapfun/BitmapFun/src/main/res/layout/image_detail_pager.xml b/samples/training/bitmapfun/BitmapFun/src/main/res/layout/image_detail_pager.xml deleted file mode 100644 index 877a26bb5..000000000 --- a/samples/training/bitmapfun/BitmapFun/src/main/res/layout/image_detail_pager.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/samples/training/bitmapfun/BitmapFun/src/main/res/layout/image_grid_fragment.xml b/samples/training/bitmapfun/BitmapFun/src/main/res/layout/image_grid_fragment.xml deleted file mode 100644 index e2034dea1..000000000 --- a/samples/training/bitmapfun/BitmapFun/src/main/res/layout/image_grid_fragment.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/samples/training/bitmapfun/BitmapFun/src/main/res/menu/main_menu.xml b/samples/training/bitmapfun/BitmapFun/src/main/res/menu/main_menu.xml deleted file mode 100644 index 35dad093f..000000000 --- a/samples/training/bitmapfun/BitmapFun/src/main/res/menu/main_menu.xml +++ /dev/null @@ -1,25 +0,0 @@ - - -

- - - - \ No newline at end of file diff --git a/samples/training/bitmapfun/BitmapFun/src/main/res/values-large/dimens.xml b/samples/training/bitmapfun/BitmapFun/src/main/res/values-large/dimens.xml deleted file mode 100644 index c68187614..000000000 --- a/samples/training/bitmapfun/BitmapFun/src/main/res/values-large/dimens.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - 148dp - 2dp - - \ No newline at end of file diff --git a/samples/training/bitmapfun/BitmapFun/src/main/res/values-v11/styles.xml b/samples/training/bitmapfun/BitmapFun/src/main/res/values-v11/styles.xml deleted file mode 100644 index 0c6452641..000000000 --- a/samples/training/bitmapfun/BitmapFun/src/main/res/values-v11/styles.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/samples/training/bitmapfun/BitmapFun/src/main/res/values-xlarge/dimens.xml b/samples/training/bitmapfun/BitmapFun/src/main/res/values-xlarge/dimens.xml deleted file mode 100644 index 0f4397751..000000000 --- a/samples/training/bitmapfun/BitmapFun/src/main/res/values-xlarge/dimens.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - 198dp - 2dp - - \ No newline at end of file diff --git a/samples/training/bitmapfun/BitmapFun/src/main/res/values/colors.xml b/samples/training/bitmapfun/BitmapFun/src/main/res/values/colors.xml deleted file mode 100644 index 7e4a4fecd..000000000 --- a/samples/training/bitmapfun/BitmapFun/src/main/res/values/colors.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - #BB7dbcd3 - #777dbcd3 - - diff --git a/samples/training/bitmapfun/BitmapFun/src/main/res/values/dimens.xml b/samples/training/bitmapfun/BitmapFun/src/main/res/values/dimens.xml deleted file mode 100644 index 60d540f02..000000000 --- a/samples/training/bitmapfun/BitmapFun/src/main/res/values/dimens.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - 100dp - 1dp - 80dp - - \ No newline at end of file diff --git a/samples/training/bitmapfun/BitmapFun/src/main/res/values/strings.xml b/samples/training/bitmapfun/BitmapFun/src/main/res/values/strings.xml deleted file mode 100644 index 8108c232f..000000000 --- a/samples/training/bitmapfun/BitmapFun/src/main/res/values/strings.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - BitmapFun - This is a sample application for the Android Training class - "Displaying Bitmaps Efficiently" - (http://developer.android.com/training/displaying-bitmaps/display-bitmap.html). It is not - designed to be a full reference application but to demonstrate the concepts discussed in - training course. - Clear Caches - Caches have been cleared - Image Thumbnail - No network connection found - - \ No newline at end of file diff --git a/samples/training/bitmapfun/BitmapFun/src/main/res/values/styles.xml b/samples/training/bitmapfun/BitmapFun/src/main/res/values/styles.xml deleted file mode 100644 index 0f1a018b9..000000000 --- a/samples/training/bitmapfun/BitmapFun/src/main/res/values/styles.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/samples/training/bitmapfun/README b/samples/training/bitmapfun/README deleted file mode 100644 index a0a192f4c..000000000 --- a/samples/training/bitmapfun/README +++ /dev/null @@ -1,8 +0,0 @@ -This is an Android Studio project: -http://developer.android.com/sdk/installing/studio.html - -First copy local.properties.sample to local.properties and set your SDK path. - -Then import the project into Android Studio: -File -> Import Project -> Choose Directory -> Import from external model -> - Gradle -> Use default gradle wrapper -> Finish diff --git a/samples/training/bitmapfun/build.gradle b/samples/training/bitmapfun/build.gradle deleted file mode 100644 index 495c5038e..000000000 --- a/samples/training/bitmapfun/build.gradle +++ /dev/null @@ -1 +0,0 @@ -// Top-level build file where you can add configuration options common to all sub-projects/modules. diff --git a/samples/training/bitmapfun/local.properties.sample b/samples/training/bitmapfun/local.properties.sample deleted file mode 100644 index 37317f492..000000000 --- a/samples/training/bitmapfun/local.properties.sample +++ /dev/null @@ -1,7 +0,0 @@ -# This file should be copied to local.properties and path set to point -# to your Android SDK. - -# Location of the SDK. This is only used by Gradle. -# For customization when using a Version Control System, please read the -# header note. -sdk.dir=/usr/local/lib/android-sdk \ No newline at end of file diff --git a/samples/training/bitmapfun/settings.gradle b/samples/training/bitmapfun/settings.gradle deleted file mode 100644 index 9f12781b6..000000000 --- a/samples/training/bitmapfun/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -include ':BitmapFun'