Merge "First commit for ListViewDraggingAnimation."
diff --git a/build/sdk-darwin-x86.atree b/build/sdk-darwin-x86.atree
index 118ed1c..53bbc1f 100644
--- a/build/sdk-darwin-x86.atree
+++ b/build/sdk-darwin-x86.atree
@@ -15,6 +15,19 @@
#
##############################################################################
+# Build Tools Component
+##############################################################################
+# Note that the build-tools sub-folder uses the platform-name as a placeholder
+# at build-time. Packaging will later change that to the actual build-tools
+# revision as specified in the source.properties.
+
+
+lib/libLLVM.dylib strip build-tools/${PLATFORM_NAME}/libLLVM.dylib
+lib/libbcc.dylib strip build-tools/${PLATFORM_NAME}/libbcc.dylib
+lib/libbcinfo.dylib strip build-tools/${PLATFORM_NAME}/libbcinfo.dylib
+lib/libclang.dylib strip build-tools/${PLATFORM_NAME}/libclang.dylib
+
+##############################################################################
# Docs Component
##############################################################################
diff --git a/build/sdk-linux-x86.atree b/build/sdk-linux-x86.atree
index e21785e..86f7754 100644
--- a/build/sdk-linux-x86.atree
+++ b/build/sdk-linux-x86.atree
@@ -14,4 +14,17 @@
# limitations under the License.
#
+##############################################################################
+# Build Tools Component
+##############################################################################
+# Note that the build-tools sub-folder uses the platform-name as a placeholder
+# at build-time. Packaging will later change that to the actual build-tools
+# revision as specified in the source.properties.
+
+
+lib/libLLVM.so strip build-tools/${PLATFORM_NAME}/libLLVM.so
+lib/libbcc.so strip build-tools/${PLATFORM_NAME}/libbcc.so
+lib/libbcinfo.so strip build-tools/${PLATFORM_NAME}/libbcinfo.so
+lib/libclang.so strip build-tools/${PLATFORM_NAME}/libclang.so
+
diff --git a/build/sdk-windows-x86.atree b/build/sdk-windows-x86.atree
index c9ec5f9..44b7fcb 100644
--- a/build/sdk-windows-x86.atree
+++ b/build/sdk-windows-x86.atree
@@ -41,6 +41,9 @@
##############################################################################
# Build Tools Component
##############################################################################
+# Note that the build-tools sub-folder uses the platform-name as a placeholder
+# at build-time. Packaging will later change that to the actual build-tools
+# revision as specified in the source.properties.
rm build-tools/${PLATFORM_NAME}/aapt
bin/aapt.exe strip build-tools/${PLATFORM_NAME}/aapt.exe
@@ -56,6 +59,20 @@
rm build-tools/${PLATFORM_NAME}/llvm-rs-cc
bin/llvm-rs-cc.exe strip build-tools/${PLATFORM_NAME}/llvm-rs-cc.exe
+rm build-tools/${PLATFORM_NAME}/libLLVM.so
+lib/libLLVM.dll strip build-tools/${PLATFORM_NAME}/libLLVM.dll
+
+rm build-tools/${PLATFORM_NAME}/libclang.so
+lib/libclang.dll strip build-tools/${PLATFORM_NAME}/libclang.dll
+
+#bcc not yet compiled on windows
+
+rm build-tools/${PLATFORM_NAME}/libbcc.so
+#lib/libbcc.dll strip build-tools/${PLATFORM_NAME}/libbcc.dll
+
+rm build-tools/${PLATFORM_NAME}/libbcinfo.so
+#lib/libbcinfo.dll strip build-tools/${PLATFORM_NAME}/libbcinfo.dll
+
##############################################################################
diff --git a/build/sdk.atree b/build/sdk.atree
index 691a934..72612af 100644
--- a/build/sdk.atree
+++ b/build/sdk.atree
@@ -73,11 +73,14 @@
# build tools from out/host/$(HOST_OS)-$(HOST_ARCH)/
bin/aapt strip build-tools/${PLATFORM_NAME}/aapt
bin/aidl strip build-tools/${PLATFORM_NAME}/aidl
+
# renderscript (cc + headers)
bin/llvm-rs-cc strip build-tools/${PLATFORM_NAME}/llvm-rs-cc
frameworks/rs/scriptc build-tools/${PLATFORM_NAME}/renderscript/include
external/clang/lib/Headers build-tools/${PLATFORM_NAME}/renderscript/clang-include
external/clang/LICENSE.TXT build-tools/${PLATFORM_NAME}/renderscript/clang-include/LICENSE.TXT
+prebuilts/sdk/renderscript/lib build-tools/${PLATFORM_NAME}/renderscript/lib
+
# dx
bin/dx build-tools/${PLATFORM_NAME}/dx
framework/dx.jar build-tools/${PLATFORM_NAME}/lib/dx.jar
@@ -180,61 +183,70 @@
#
# the list here should match the list of samples that we generate docs for,
# (see web_docs_sample_code_flags in frameworks/base/Android.mk)
-development/apps/GestureBuilder samples/${PLATFORM_NAME}/GestureBuilder
+development/apps/GestureBuilder samples/${PLATFORM_NAME}/legacy/GestureBuilder
development/samples/samples_source.properties samples/${PLATFORM_NAME}/source.properties
#
# PLEASE KEEP THE SAMPLES IN ALPHABETICAL ORDER.
#
-development/samples/AccelerometerPlay samples/${PLATFORM_NAME}/AccelerometerPlay
-development/samples/ActionBarCompat samples/${PLATFORM_NAME}/ActionBarCompat
-development/samples/AndroidBeamDemo samples/${PLATFORM_NAME}/AndroidBeamDemo
-development/samples/ApiDemos samples/${PLATFORM_NAME}/ApiDemos
-development/samples/AppNavigation samples/${PLATFORM_NAME}/AppNavigation
-development/samples/AppRestrictions samples/${PLATFORM_NAME}/AppRestrictions
-development/samples/BackupRestore samples/${PLATFORM_NAME}/BackupRestore
-development/samples/BasicGLSurfaceView samples/${PLATFORM_NAME}/BasicGLSurfaceView
-development/samples/BluetoothChat samples/${PLATFORM_NAME}/BluetoothChat
-development/samples/BluetoothHDP samples/${PLATFORM_NAME}/BluetoothHDP
-development/samples/BluetoothLeGatt samples/${PLATFORM_NAME}/BluetoothLeGatt
-development/samples/ContactManager samples/${PLATFORM_NAME}/ContactManager
-development/samples/CrossCompatibility samples/${PLATFORM_NAME}/CrossCompatibility
-development/samples/CubeLiveWallpaper samples/${PLATFORM_NAME}/CubeLiveWallpaper
-development/samples/HelloEffects samples/${PLATFORM_NAME}/HelloEffects
-development/samples/Home samples/${PLATFORM_NAME}/Home
-development/samples/HoneycombGallery samples/${PLATFORM_NAME}/HoneycombGallery
-development/samples/JetBoy samples/${PLATFORM_NAME}/JetBoy
-development/samples/KeyChainDemo samples/${PLATFORM_NAME}/KeyChainDemo
-development/samples/LunarLander samples/${PLATFORM_NAME}/LunarLander
-development/samples/MultiResolution samples/${PLATFORM_NAME}/MultiResolution
-development/samples/NotePad samples/${PLATFORM_NAME}/NotePad
-development/samples/RandomMusicPlayer samples/${PLATFORM_NAME}/RandomMusicPlayer
-development/samples/SpellChecker/SampleSpellCheckerService samples/${PLATFORM_NAME}/SpellChecker/SampleSpellCheckerService
-development/samples/SpellChecker/HelloSpellChecker samples/${PLATFORM_NAME}/SpellChecker/HelloSpellChecker
-development/samples/SampleSyncAdapter samples/${PLATFORM_NAME}/SampleSyncAdapter
-development/samples/SearchableDictionary samples/${PLATFORM_NAME}/SearchableDictionary
-development/samples/SipDemo samples/${PLATFORM_NAME}/SipDemo
-development/samples/SkeletonApp samples/${PLATFORM_NAME}/SkeletonApp
-development/samples/Snake samples/${PLATFORM_NAME}/Snake
-development/samples/SoftKeyboard samples/${PLATFORM_NAME}/SoftKeyboard
-development/samples/Spinner samples/${PLATFORM_NAME}/Spinner
-development/samples/SpinnerTest samples/${PLATFORM_NAME}/SpinnerTest
-development/samples/TicTacToeLib samples/${PLATFORM_NAME}/TicTacToeLib
-development/samples/TicTacToeMain samples/${PLATFORM_NAME}/TicTacToeMain
-development/samples/TtsEngine samples/${PLATFORM_NAME}/TtsEngine
-development/samples/ToyVpn samples/${PLATFORM_NAME}/ToyVpn
-development/samples/UiAutomator samples/${PLATFORM_NAME}/UiAutomator
-development/samples/USB/MissileLauncher samples/${PLATFORM_NAME}/USB/MissileLauncher
-development/samples/USB/AdbTest samples/${PLATFORM_NAME}/USB/AdbTest
-development/samples/VoiceRecognitionService samples/${PLATFORM_NAME}/VoiceRecognitionService
-development/samples/VoicemailProviderDemo samples/${PLATFORM_NAME}/VoicemailProviderDemo
-development/samples/WeatherListWidget samples/${PLATFORM_NAME}/WeatherListWidget
-development/apps/WidgetPreview samples/${PLATFORM_NAME}/WidgetPreview
-development/samples/WiFiDirectDemo samples/${PLATFORM_NAME}/WiFiDirectDemo
-development/samples/WiFiDirectServiceDiscovery samples/${PLATFORM_NAME}/WiFiDirectServiceDiscovery
-development/samples/Wiktionary samples/${PLATFORM_NAME}/Wiktionary
-development/samples/WiktionarySimple samples/${PLATFORM_NAME}/WiktionarySimple
-development/samples/XmlAdapters samples/${PLATFORM_NAME}/XmlAdapters
-development/samples/RenderScript/HelloCompute samples/${PLATFORM_NAME}/RenderScript/HelloCompute
+
+# New sample tree
+developers/samples/android/connectivity samples/${PLATFORM_NAME}/connectivity
+developers/samples/android/content samples/${PLATFORM_NAME}/content
+developers/samples/android/input samples/${PLATFORM_NAME}/input
+developers/samples/android/media samples/${PLATFORM_NAME}/media
+developers/samples/android/security samples/${PLATFORM_NAME}/security
+developers/samples/android/testing samples/${PLATFORM_NAME}/testing
+developers/samples/android/ui samples/${PLATFORM_NAME}/ui
+
+# Old sample tree
+development/samples/AccelerometerPlay samples/${PLATFORM_NAME}/legacy/AccelerometerPlay
+development/samples/ActionBarCompat samples/${PLATFORM_NAME}/legacy/ActionBarCompat
+development/samples/AndroidBeamDemo samples/${PLATFORM_NAME}/legacy/AndroidBeamDemo
+development/samples/ApiDemos samples/${PLATFORM_NAME}/legacy/ApiDemos
+development/samples/AppNavigation samples/${PLATFORM_NAME}/legacy/AppNavigation
+development/samples/BackupRestore samples/${PLATFORM_NAME}/legacy/BackupRestore
+development/samples/BasicGLSurfaceView samples/${PLATFORM_NAME}/legacy/BasicGLSurfaceView
+development/samples/BluetoothChat samples/${PLATFORM_NAME}/legacy/BluetoothChat
+development/samples/BluetoothHDP samples/${PLATFORM_NAME}/legacy/BluetoothHDP
+development/samples/ContactManager samples/${PLATFORM_NAME}/legacy/ContactManager
+development/samples/CrossCompatibility samples/${PLATFORM_NAME}/legacy/CrossCompatibility
+development/samples/CubeLiveWallpaper samples/${PLATFORM_NAME}/legacy/CubeLiveWallpaper
+development/samples/HelloEffects samples/${PLATFORM_NAME}/legacy/HelloEffects
+development/samples/Home samples/${PLATFORM_NAME}/legacy/Home
+development/samples/HoneycombGallery samples/${PLATFORM_NAME}/legacy/HoneycombGallery
+development/samples/JetBoy samples/${PLATFORM_NAME}/legacy/JetBoy
+development/samples/KeyChainDemo samples/${PLATFORM_NAME}/legacy/KeyChainDemo
+development/samples/LunarLander samples/${PLATFORM_NAME}/legacy/LunarLander
+development/samples/MultiResolution samples/${PLATFORM_NAME}/legacy/MultiResolution
+development/samples/NotePad samples/${PLATFORM_NAME}/legacy/NotePad
+development/samples/RandomMusicPlayer samples/${PLATFORM_NAME}/legacy/RandomMusicPlayer
+development/samples/SpellChecker/SampleSpellCheckerService samples/${PLATFORM_NAME}/legacy/SpellChecker/SampleSpellCheckerService
+development/samples/SpellChecker/HelloSpellChecker samples/${PLATFORM_NAME}/legacy/SpellChecker/HelloSpellChecker
+development/samples/SampleSyncAdapter samples/${PLATFORM_NAME}/legacy/SampleSyncAdapter
+development/samples/SearchableDictionary samples/${PLATFORM_NAME}/legacy/SearchableDictionary
+development/samples/SipDemo samples/${PLATFORM_NAME}/legacy/SipDemo
+development/samples/SkeletonApp samples/${PLATFORM_NAME}/legacy/SkeletonApp
+development/samples/Snake samples/${PLATFORM_NAME}/legacy/Snake
+development/samples/SoftKeyboard samples/${PLATFORM_NAME}/legacy/SoftKeyboard
+development/samples/Spinner samples/${PLATFORM_NAME}/legacy/Spinner
+development/samples/SpinnerTest samples/${PLATFORM_NAME}/legacy/SpinnerTest
+development/samples/TicTacToeLib samples/${PLATFORM_NAME}/legacy/TicTacToeLib
+development/samples/TicTacToeMain samples/${PLATFORM_NAME}/legacy/TicTacToeMain
+development/samples/TtsEngine samples/${PLATFORM_NAME}/legacy/TtsEngine
+development/samples/ToyVpn samples/${PLATFORM_NAME}/legacy/ToyVpn
+development/samples/UiAutomator samples/${PLATFORM_NAME}/legacy/UiAutomator
+development/samples/USB/MissileLauncher samples/${PLATFORM_NAME}/legacy/USB/MissileLauncher
+development/samples/USB/AdbTest samples/${PLATFORM_NAME}/legacy/USB/AdbTest
+development/samples/VoiceRecognitionService samples/${PLATFORM_NAME}/legacy/VoiceRecognitionService
+development/samples/VoicemailProviderDemo samples/${PLATFORM_NAME}/legacy/VoicemailProviderDemo
+development/samples/WeatherListWidget samples/${PLATFORM_NAME}/legacy/WeatherListWidget
+development/apps/WidgetPreview samples/${PLATFORM_NAME}/legacy/WidgetPreview
+development/samples/WiFiDirectDemo samples/${PLATFORM_NAME}/legacy/WiFiDirectDemo
+development/samples/WiFiDirectServiceDiscovery samples/${PLATFORM_NAME}/legacy/WiFiDirectServiceDiscovery
+development/samples/Wiktionary samples/${PLATFORM_NAME}/legacy/Wiktionary
+development/samples/WiktionarySimple samples/${PLATFORM_NAME}/legacy/WiktionarySimple
+development/samples/XmlAdapters samples/${PLATFORM_NAME}/legacy/XmlAdapters
+development/samples/RenderScript/HelloCompute samples/${PLATFORM_NAME}/legacy/RenderScript/HelloCompute
# NOTICE files are copied by build/core/Makefile from sdk.git
sdk/files/sdk_files_NOTICE.txt samples/${PLATFORM_NAME}/NOTICE.txt
diff --git a/ndk/platforms/android-9/arch-x86/src/__stack_chk_fail_local.h b/ndk/platforms/android-9/arch-x86/src/__stack_chk_fail_local.h
new file mode 100644
index 0000000..4f3699a
--- /dev/null
+++ b/ndk/platforms/android-9/arch-x86/src/__stack_chk_fail_local.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+/*
+ __stack_chk_fail routine is runtime part of stack protector compiler
+ feature. It's implemented in libc and represents die routine when stack
+ corruption is detected.
+
+ Calls are generated by compiler and injected into user functions when
+ -fstack-protector* options are used.
+
+ __stack_chk_fail_local is wrapper for __stack_chk_fail. Compiler generates
+ wrapper calls instead for PIC code only and only on IA32 for optimization
+ purpose (see gcc/config/i386/i386.c). Wrapper body is always included into
+ executable or library. This is the idea of optimization.
+
+ Glibc is doing this via libc_nonshared.a which is linked automatically
+ everytime with libc.so. In bionic we have to bring it within crtfiles
+ because libc.so is real library and not a link script like libc.so at glibc.
+
+ For x86_64 or non-PIC code compiler always generates __stack_chk_fail calls.
+*/
+
+#ifdef __i386__
+#ifdef __PIC__
+extern void __stack_chk_fail();
+
+__attribute__ ((visibility ("hidden")))
+void __stack_chk_fail_local()
+{
+ __stack_chk_fail();
+}
+#endif
+#endif
diff --git a/ndk/platforms/android-9/arch-x86/src/atexit.S b/ndk/platforms/android-9/arch-x86/src/atexit.S
deleted file mode 100644
index b28f40b..0000000
--- a/ndk/platforms/android-9/arch-x86/src/atexit.S
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
- .text
- .p2align 4,,15
- .globl atexit
- .hidden atexit
- .type atexit, @function
-atexit:
- pushl %ebp
- movl %esp, %ebp
- pushl %ebx
- call __x86.get_pc_thunk.bx
- addl $_GLOBAL_OFFSET_TABLE_, %ebx
- subl $20, %esp
- movl $0, 4(%esp)
- movl __dso_handle@GOTOFF(%ebx), %eax
- movl %eax, 8(%esp)
- movl 8(%ebp), %eax
- movl %eax, (%esp)
- call __cxa_atexit@PLT
- addl $20, %esp
- popl %ebx
- popl %ebp
- ret
- .size atexit, .-atexit
-
- .section .text.__x86.get_pc_thunk.bx,"axG",@progbits,__x86.get_pc_thunk.bx,comdat
- .globl __x86.get_pc_thunk.bx
- .hidden __x86.get_pc_thunk.bx
- .type __x86.get_pc_thunk.bx, @function
-__x86.get_pc_thunk.bx:
- nop
- nop
- nop
- nop
- nop
- nop
- nop
- nop
- movl (%esp), %ebx
- ret
diff --git a/ndk/platforms/android-9/arch-x86/src/__stack_chk_fail_local.S b/ndk/platforms/android-9/arch-x86/src/atexit.h
similarity index 73%
rename from ndk/platforms/android-9/arch-x86/src/__stack_chk_fail_local.S
rename to ndk/platforms/android-9/arch-x86/src/atexit.h
index 59fe86e..bc776a8 100644
--- a/ndk/platforms/android-9/arch-x86/src/__stack_chk_fail_local.S
+++ b/ndk/platforms/android-9/arch-x86/src/atexit.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 The Android Open Source Project
+ * Copyright (C) 2012 The Android Open Source Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -25,24 +25,11 @@
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
-/*
- * Contributed by: Intel Corporation
- */
- .text
- .p2align 4,,15
- .globl __stack_chk_fail_local
- .hidden __stack_chk_fail_local
- .type __stack_chk_fail_local, @function
+extern void *__dso_handle;
-__stack_chk_fail_local:
-#ifdef __PIC__
- pushl %ebx
- call __x86.get_pc_thunk.bx
- addl $_GLOBAL_OFFSET_TABLE_, %ebx
- call __stack_chk_fail@PLT
-#else /* PIC */
- jmp __stack_chk_fail
-#endif /* not PIC */
-
- .size __stack_chk_fail_local, .-__stack_chk_fail_local
+__attribute__ ((visibility ("hidden")))
+int atexit(void (*func)(void))
+{
+ return (__cxa_atexit((void (*)(void *))func, (void *)0, &__dso_handle));
+}
diff --git a/ndk/platforms/android-9/arch-x86/src/__stack_chk_fail_local.S b/ndk/platforms/android-9/arch-x86/src/crtbegin.c
similarity index 60%
copy from ndk/platforms/android-9/arch-x86/src/__stack_chk_fail_local.S
copy to ndk/platforms/android-9/arch-x86/src/crtbegin.c
index 59fe86e..43e9306 100644
--- a/ndk/platforms/android-9/arch-x86/src/__stack_chk_fail_local.S
+++ b/ndk/platforms/android-9/arch-x86/src/crtbegin.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 The Android Open Source Project
+ * Copyright (C) 2012 The Android Open Source Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -25,24 +25,32 @@
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
-/*
- * Contributed by: Intel Corporation
- */
- .text
- .p2align 4,,15
- .globl __stack_chk_fail_local
- .hidden __stack_chk_fail_local
- .type __stack_chk_fail_local, @function
+#include "../../bionic/libc_init_common.h"
+#include <stddef.h>
+#include <stdint.h>
-__stack_chk_fail_local:
-#ifdef __PIC__
- pushl %ebx
- call __x86.get_pc_thunk.bx
- addl $_GLOBAL_OFFSET_TABLE_, %ebx
- call __stack_chk_fail@PLT
-#else /* PIC */
- jmp __stack_chk_fail
-#endif /* not PIC */
+__attribute__ ((section (".preinit_array")))
+void (*__PREINIT_ARRAY__)(void) = (void (*)(void)) -1;
- .size __stack_chk_fail_local, .-__stack_chk_fail_local
+__attribute__ ((section (".init_array")))
+void (*__INIT_ARRAY__)(void) = (void (*)(void)) -1;
+
+__attribute__ ((section (".fini_array")))
+void (*__FINI_ARRAY__)(void) = (void (*)(void)) -1;
+
+__LIBC_HIDDEN__
+__attribute__((force_align_arg_pointer))
+void _start() {
+ structors_array_t array;
+ array.preinit_array = &__PREINIT_ARRAY__;
+ array.init_array = &__INIT_ARRAY__;
+ array.fini_array = &__FINI_ARRAY__;
+
+ void* raw_args = (void*) ((uintptr_t) __builtin_frame_address(0) + sizeof(void*));
+ __libc_init(raw_args, NULL, &main, &array);
+}
+
+#include "__dso_handle.h"
+#include "atexit.h"
+#include "__stack_chk_fail_local.h"
diff --git a/ndk/platforms/android-9/arch-x86/src/crtbegin_dynamic.S b/ndk/platforms/android-9/arch-x86/src/crtbegin_dynamic.S
deleted file mode 100644
index 0ccad70..0000000
--- a/ndk/platforms/android-9/arch-x86/src/crtbegin_dynamic.S
+++ /dev/null
@@ -1,138 +0,0 @@
-# bionic/arch-x86/bionic/crtbegin_dynamic.S
-#
-# Copyright 2006, The Android Open Source Project
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-# * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above copyright
-# notice, this list of conditions and the following disclaimer in the
-# documentation and/or other materials provided with the distribution.
-# * Neither the name of Google Inc. nor the names of its contributors may
-# be used to endorse or promote products derived from this software
-# without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR
-# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
-# EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
-# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
-# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
-# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
-# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
- .text
- .align 4
- .type _start, @function
- .globl _start
-
-# this is the small startup code that is first run when
-# any executable that is dynamically-linked with Bionic
-# runs.
-#
-# it's purpose is to call __libc_init with appropriate
-# arguments, which are:
-#
-# - the address of the raw data block setup by the Linux
-# kernel ELF loader
-#
-# - address of an "onexit" function, not used on any
-# platform supported by Bionic
-#
-# - address of the "main" function of the program. We
-# can't hard-code it in the adr pseudo instruction
-# so we use a tiny trampoline that will get relocated
-# by the dynamic linker before this code runs
-#
-# - address of the constructor list
-#
-_start:
- mov %esp, %eax
- # before push arguments, align the stack to a 16 byte boundary
- andl $~15, %esp
- mov $1f, %edx
- pushl %edx
- mov $0f, %edx
- pushl %edx
- mov $0, %edx
- pushl %edx
- pushl %eax
- call __libc_init
-
-0: jmp main
-
-1: .long __PREINIT_ARRAY__
- .long __INIT_ARRAY__
- .long __FINI_ARRAY__
-
- .section .preinit_array, "aw"
- .globl __PREINIT_ARRAY__
-__PREINIT_ARRAY__:
- .long -1
-
- .section .init_array, "aw"
- .globl __INIT_ARRAY__
-__INIT_ARRAY__:
- .long -1
- .long frame_dummy
-
- .section .fini_array, "aw"
- .globl __FINI_ARRAY__
-__FINI_ARRAY__:
- .long -1
- .long __do_global_dtors_aux
-
- .section .eh_frame,"a",@progbits
- .align 4
- .type __EH_FRAME_BEGIN__, @object
-__EH_FRAME_BEGIN__:
- .text
- .p2align 4,,15
- .type __do_global_dtors_aux, @function
-__do_global_dtors_aux:
- pushl %ebp
- movl %esp, %ebp
- subl $24, %esp
- cmpb $0, completed.4454
- jne .L4
- movl $__deregister_frame_info_bases, %eax
- testl %eax, %eax
- je .L3
- movl $__EH_FRAME_BEGIN__, (%esp)
- call __deregister_frame_info_bases
-.L3:
- movb $1, completed.4454
-.L4:
- leave
- ret
- .text
- .p2align 4,,15
- .type frame_dummy, @function
-frame_dummy:
- pushl %ebp
- movl $__register_frame_info_bases, %eax
- movl %esp, %ebp
- subl $24, %esp
- testl %eax, %eax
- je .L7
- movl %ebx, 12(%esp)
- movl $0, 8(%esp)
- movl $object.4466, 4(%esp)
- movl $__EH_FRAME_BEGIN__, (%esp)
- call __register_frame_info_bases
-.L7:
- leave
- ret
- .local completed.4454
- .comm completed.4454,1,1
- .local object.4466
- .comm object.4466,24,4
- .weak __register_frame_info_bases
- .weak __deregister_frame_info_bases
-
-#include "__dso_handle.S"
-#include "atexit.S"
-#include "__stack_chk_fail_local.S"
diff --git a/ndk/platforms/android-9/arch-x86/src/crtbegin_so.S b/ndk/platforms/android-9/arch-x86/src/crtbegin_so.S
deleted file mode 100644
index 99662fe..0000000
--- a/ndk/platforms/android-9/arch-x86/src/crtbegin_so.S
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-.section .init_array, "aw"
-.align 4
-.type __INIT_ARRAY__, @object
-.globl __INIT_ARRAY__
-__INIT_ARRAY__:
- .long -1
- .long frame_dummy
-
-.section .fini_array, "aw"
-.align 4
-.type __FINI_ARRAY__, @object
-.globl __FINI_ARRAY__
-__FINI_ARRAY__:
- .long -1
- .long __do_global_dtors_aux
-
- .section .eh_frame,"a",@progbits
- .align 4
- .type __EH_FRAME_BEGIN__, @object
-__EH_FRAME_BEGIN__:
- .text
- .p2align 4,,15
- .type __do_global_dtors_aux, @function
-__do_global_dtors_aux:
- pushl %ebp
- movl %esp, %ebp
- pushl %ebx
- call __x86.get_pc_thunk.bx
- addl $_GLOBAL_OFFSET_TABLE_, %ebx
- subl $20, %esp
- cmpb $0, completed.4454@GOTOFF(%ebx)
- jne .L5
- movl __dso_handle@GOTOFF(%ebx), %eax
- movl %eax, (%esp)
- call __cxa_finalize@PLT
- movl __deregister_frame_info_bases@GOT(%ebx), %eax
- testl %eax, %eax
- je .L4
- leal __EH_FRAME_BEGIN__@GOTOFF(%ebx), %eax
- movl %eax, (%esp)
- call __deregister_frame_info_bases@PLT
-.L4:
- movb $1, completed.4454@GOTOFF(%ebx)
-.L5:
- addl $20, %esp
- popl %ebx
- popl %ebp
- ret
- .text
- .p2align 4,,15
- .type frame_dummy, @function
-frame_dummy:
- pushl %ebp
- movl %esp, %ebp
- pushl %ebx
- call __x86.get_pc_thunk.bx
- addl $_GLOBAL_OFFSET_TABLE_, %ebx
- subl $20, %esp
- movl __register_frame_info_bases@GOT(%ebx), %eax
- testl %eax, %eax
- je .L8
- leal object.4469@GOTOFF(%ebx), %eax
- movl %eax, 4(%esp)
- leal __EH_FRAME_BEGIN__@GOTOFF(%ebx), %eax
- movl %ebx, 12(%esp)
- movl $0, 8(%esp)
- movl %eax, (%esp)
- call __register_frame_info_bases@PLT
-.L8:
- addl $20, %esp
- popl %ebx
- popl %ebp
- ret
- .local completed.4454
- .comm completed.4454,1,1
- .local object.4469
- .comm object.4469,24,4
- .weak __register_frame_info_bases
- .weak __deregister_frame_info_bases
-
-#include "__dso_handle_so.S"
-#include "atexit.S"
-#include "__stack_chk_fail_local.S"
diff --git a/ndk/platforms/android-9/arch-x86/src/crtbegin_so.c b/ndk/platforms/android-9/arch-x86/src/crtbegin_so.c
new file mode 100644
index 0000000..30de6af
--- /dev/null
+++ b/ndk/platforms/android-9/arch-x86/src/crtbegin_so.c
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+extern void __cxa_finalize(void *);
+extern void *__dso_handle;
+
+__attribute__((visibility("hidden"),destructor))
+void __on_dlclose() {
+ __cxa_finalize(&__dso_handle);
+}
+
+/* CRT_LEGACY_WORKAROUND should only be defined when building
+ * this file as part of the platform's C library.
+ *
+ * The C library already defines a function named 'atexit()'
+ * for backwards compatibility with older NDK-generated binaries.
+ *
+ * For newer ones, 'atexit' is actually embedded in the C
+ * runtime objects that are linked into the final ELF
+ * binary (shared library or executable), and will call
+ * __cxa_atexit() in order to un-register any atexit()
+ * handler when a library is unloaded.
+ *
+ * This function must be global *and* hidden. Only the
+ * code inside the same ELF binary should be able to access it.
+ */
+
+#ifdef CRT_LEGACY_WORKAROUND
+#include "__dso_handle.h"
+#else
+#include "__dso_handle_so.h"
+#include "atexit.h"
+#include "__stack_chk_fail_local.h"
+#endif
diff --git a/ndk/platforms/android-9/arch-x86/src/crtbegin_static.S b/ndk/platforms/android-9/arch-x86/src/crtbegin_static.S
deleted file mode 100644
index 4fffecd..0000000
--- a/ndk/platforms/android-9/arch-x86/src/crtbegin_static.S
+++ /dev/null
@@ -1,138 +0,0 @@
-# bionic/arch-x86/bionic/crtbegin_static.S
-#
-# Copyright 2006, The Android Open Source Project
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-# * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above copyright
-# notice, this list of conditions and the following disclaimer in the
-# documentation and/or other materials provided with the distribution.
-# * Neither the name of Google Inc. nor the names of its contributors may
-# be used to endorse or promote products derived from this software
-# without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR
-# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
-# EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
-# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
-# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
-# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
-# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
- .text
- .align 4
- .type _start, @function
- .globl _start
-
-# this is the small startup code that is first run when
-# any executable that is statically-linked with Bionic
-# runs.
-#
-# it's purpose is to call __libc_init with appropriate
-# arguments, which are:
-#
-# - the address of the raw data block setup by the Linux
-# kernel ELF loader
-#
-# - address of an "onexit" function, not used on any
-# platform supported by Bionic
-#
-# - address of the "main" function of the program. We
-# can't hard-code it in the adr pseudo instruction
-# so we use a tiny trampoline that will get relocated
-# by the dynamic linker before this code runs
-#
-# - address of the constructor list
-#
-_start:
- mov %esp, %eax
- # before push arguments, align the stack to a 16 byte boundary
- andl $~15, %esp
- mov $1f, %edx
- pushl %edx
- mov $0f, %edx
- pushl %edx
- mov $0, %edx
- pushl %edx
- pushl %eax
- call __libc_init
-
-0: jmp main
-
-1: .long __PREINIT_ARRAY__
- .long __INIT_ARRAY__
- .long __FINI_ARRAY__
-
- .section .preinit_array, "aw"
- .globl __PREINIT_ARRAY__
-__PREINIT_ARRAY__:
- .long -1
-
- .section .init_array, "aw"
- .globl __INIT_ARRAY__
-__INIT_ARRAY__:
- .long -1
- .long frame_dummy
-
- .section .fini_array, "aw"
- .globl __FINI_ARRAY__
-__FINI_ARRAY__:
- .long -1
- .long __do_global_dtors_aux
-
- .section .eh_frame,"a",@progbits
- .align 4
- .type __EH_FRAME_BEGIN__, @object
-__EH_FRAME_BEGIN__:
- .text
- .p2align 4,,15
- .type __do_global_dtors_aux, @function
-__do_global_dtors_aux:
- pushl %ebp
- movl %esp, %ebp
- subl $24, %esp
- cmpb $0, completed.4454
- jne .L4
- movl $__deregister_frame_info_bases, %eax
- testl %eax, %eax
- je .L3
- movl $__EH_FRAME_BEGIN__, (%esp)
- call __deregister_frame_info_bases
-.L3:
- movb $1, completed.4454
-.L4:
- leave
- ret
- .text
- .p2align 4,,15
- .type frame_dummy, @function
-frame_dummy:
- pushl %ebp
- movl $__register_frame_info_bases, %eax
- movl %esp, %ebp
- subl $24, %esp
- testl %eax, %eax
- je .L7
- movl %ebx, 12(%esp)
- movl $0, 8(%esp)
- movl $object.4466, 4(%esp)
- movl $__EH_FRAME_BEGIN__, (%esp)
- call __register_frame_info_bases
-.L7:
- leave
- ret
- .local completed.4454
- .comm completed.4454,1,1
- .local object.4466
- .comm object.4466,24,4
- .weak __register_frame_info_bases
- .weak __deregister_frame_info_bases
-
-#include "__dso_handle.S"
-#include "atexit.S"
-#include "__stack_chk_fail_local.S"
diff --git a/ndk/platforms/android-9/arch-x86_64 b/ndk/platforms/android-9/arch-x86_64
new file mode 120000
index 0000000..fb7033c
--- /dev/null
+++ b/ndk/platforms/android-9/arch-x86_64
@@ -0,0 +1 @@
+arch-x86
\ No newline at end of file
diff --git a/samples/AppRestrictions/Android.mk b/samples/AppRestrictions/Android.mk
deleted file mode 100644
index 6ab20da..0000000
--- a/samples/AppRestrictions/Android.mk
+++ /dev/null
@@ -1,17 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := samples tests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_PACKAGE_NAME := AppRestrictions
-
-LOCAL_SDK_VERSION := current
-
-include $(BUILD_PACKAGE)
-
-LOCAL_PROGUARD_FLAG_FILES := proguard.flags
-
-# Use the following include to make our test apk.
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/samples/AppRestrictions/AndroidManifest.xml b/samples/AppRestrictions/AndroidManifest.xml
deleted file mode 100644
index dd9fc09..0000000
--- a/samples/AppRestrictions/AndroidManifest.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- android:versionCode="1"
- android:versionName="1"
- package="com.example.android.apprestrictions">
-
- <uses-sdk android:minSdkVersion="18" />
-
- <application android:label="@string/app_name"
- android:icon="@drawable/ic_launcher">
-
- <activity android:name="MainActivity"
- android:label="@string/app_name" >
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
-
- <activity android:name="CustomRestrictionsActivity"
- android:label="@string/restrictions_activity_label" />
-
- <receiver android:name="GetRestrictionsReceiver">
- <intent-filter>
- <action android:name="android.intent.action.GET_RESTRICTION_ENTRIES" />
- </intent-filter>
- </receiver>
- </application>
-</manifest>
diff --git a/samples/AppRestrictions/res/drawable-hdpi/ic_launcher.png b/samples/AppRestrictions/res/drawable-hdpi/ic_launcher.png
deleted file mode 100755
index f36c473..0000000
--- a/samples/AppRestrictions/res/drawable-hdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/samples/AppRestrictions/res/drawable-mdpi/ic_launcher.png b/samples/AppRestrictions/res/drawable-mdpi/ic_launcher.png
deleted file mode 100755
index 5ab2e0d..0000000
--- a/samples/AppRestrictions/res/drawable-mdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/samples/AppRestrictions/res/drawable-xhdpi/ic_launcher.png b/samples/AppRestrictions/res/drawable-xhdpi/ic_launcher.png
deleted file mode 100755
index 7622838..0000000
--- a/samples/AppRestrictions/res/drawable-xhdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/samples/AppRestrictions/res/drawable-xxhdpi/ic_launcher.png b/samples/AppRestrictions/res/drawable-xxhdpi/ic_launcher.png
deleted file mode 100755
index 7f55fef..0000000
--- a/samples/AppRestrictions/res/drawable-xxhdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/samples/AppRestrictions/res/layout/main.xml b/samples/AppRestrictions/res/layout/main.xml
deleted file mode 100644
index 55e2c8e..0000000
--- a/samples/AppRestrictions/res/layout/main.xml
+++ /dev/null
@@ -1,114 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- <LinearLayout android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_margin="20dp">
-
- <TextView android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginBottom="10dp"
- android:textSize="18sp"
- android:text="@string/sample_app_description"/>
-
- <LinearLayout android:orientation="horizontal"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginBottom="15dp">
- <CheckBox android:id="@+id/custom_app_limits"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:onClick="onCustomClicked"/>
- <TextView android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textSize="18sp"
- android:text="@string/custom_description"
- android:onClick="onCustomClicked"
- android:layout_weight="1"/>
- </LinearLayout>
-
- <!-- Separator -->
- <View android:layout_height="1dp"
- android:background="@android:color/white"
- android:layout_width="match_parent"
- android:layout_margin="25dp"/>
-
- <!-- Section to show app restriction settings under a restricted profile. -->
- <TextView android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textSize="20sp"
- android:text="@string/current_app_limits_label"/>
- <TextView android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textSize="18sp"
- android:layout_marginBottom="10dp"
- android:text="@string/current_app_limits_description"/>
-
- <LinearLayout android:orientation="horizontal"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="20dp">
- <TextView android:layout_width="210dp"
- android:layout_height="wrap_content"
- android:textSize="18sp"
- android:text="@string/boolean_entry_title"/>
- <Space android:layout_height="1dp"
- android:layout_width="15dp"/>
- <TextView android:id="@+id/boolean_entry_id"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textSize="18sp"
- android:text="@string/boolean_entry_title"/>
- </LinearLayout>
-
- <LinearLayout android:orientation="horizontal"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="20dp">
- <TextView android:layout_width="210dp"
- android:layout_height="wrap_content"
- android:textSize="18sp"
- android:text="@string/choice_entry_title"/>
- <Space android:layout_height="1dp"
- android:layout_width="15dp"/>
- <TextView android:id="@+id/choice_entry_id"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textSize="18sp"
- android:text="@string/boolean_entry_title"/>
- </LinearLayout>
-
- <LinearLayout android:orientation="horizontal"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="20dp">
- <TextView android:layout_width="210dp"
- android:layout_height="wrap_content"
- android:textSize="18sp"
- android:text="@string/multi_entry_title"/>
- <Space android:layout_height="1dp"
- android:layout_width="15dp"/>
- <TextView android:id="@+id/multi_entry_id"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textSize="18sp"
- android:text="@string/multi_entry_title"/>
- </LinearLayout>
- </LinearLayout>
-</ScrollView>
\ No newline at end of file
diff --git a/samples/AppRestrictions/res/values/strings.xml b/samples/AppRestrictions/res/values/strings.xml
deleted file mode 100644
index 2dac3b1..0000000
--- a/samples/AppRestrictions/res/values/strings.xml
+++ /dev/null
@@ -1,65 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name">App Restrictions Demo</string>
- <string name="restrictions_activity_label">Custom app restrictions</string>
- <string name="boolean_entry_title">Test boolean type</string>
- <string name="choice_entry_title">Test choice type</string>
- <string name="multi_entry_title">Test multi-select type</string>
- <string name="custom_description">If checked, use a custom app restriction Activity. Otherwise,
- use standard restriction types.
- </string>
- <string name="sample_app_description">Note: This sample app requires the restricted profile
- feature.\n\n
- 1. If this is the primary user, go to Settings > Users.\n\n
- 2. Create a restricted profile, if one doesn\'t exist already.\n\n
- 3. Open the profile settings, locate the sample app, and tap the app restriction settings
- icon. Configure app restrictions for the app.\n\n
- 4. In the lock screen, switch to the user\'s restricted profile, launch this sample app,
- and see the configured app restrictions displayed.\n
- </string>
- <string name="settings_button_label">Go to Settings</string>
- <string name="current_app_limits_label">Current app restriction settings:</string>
- <string name="na">N/A</string>
- <string name="current_app_limits_description">Your app can restrict its content based on these
- settings, which can be configured through the primary user\'s Users Settings.
- </string>
-
- <string-array name="multi_entry_entries">
- <item>Ice Cream</item>
- <item>Jelly Bean</item>
- <item>More Jelly Bean</item>
- </string-array>
-
- <string-array name="multi_entry_values" translateable="false">
- <item>1</item>
- <item>2</item>
- <item>3</item>
- </string-array>
-
- <string-array name="choice_entry_entries">
- <item>Ice Cream</item>
- <item>Jelly Bean</item>
- <item>More Jelly Bean</item>
- </string-array>
-
- <string-array name="choice_entry_values" translateable="false">
- <item>1</item>
- <item>2</item>
- <item>3</item>
- </string-array>
-
-</resources>
\ No newline at end of file
diff --git a/samples/AppRestrictions/res/xml/custom_prefs.xml b/samples/AppRestrictions/res/xml/custom_prefs.xml
deleted file mode 100644
index 5a3cf0d..0000000
--- a/samples/AppRestrictions/res/xml/custom_prefs.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
- android:title="@string/restrictions_activity_label">
-
- <CheckBoxPreference android:key="pref_boolean"
- android:title="@string/boolean_entry_title" />
-
- <ListPreference android:key="pref_choice"
- android:title="@string/choice_entry_title"
- android:entries="@array/choice_entry_entries"
- android:entryValues="@array/choice_entry_values" />
-
- <MultiSelectListPreference android:key="pref_multi"
- android:title="@string/multi_entry_title"
- android:entries="@array/multi_entry_entries"
- android:entryValues="@array/multi_entry_values" />
-
-</PreferenceScreen>
diff --git a/samples/AppRestrictions/src/com/example/android/apprestrictions/CustomRestrictionsActivity.java b/samples/AppRestrictions/src/com/example/android/apprestrictions/CustomRestrictionsActivity.java
deleted file mode 100644
index 213b313..0000000
--- a/samples/AppRestrictions/src/com/example/android/apprestrictions/CustomRestrictionsActivity.java
+++ /dev/null
@@ -1,44 +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.apprestrictions;
-
-import android.app.Activity;
-import android.os.Bundle;
-
-/**
- * This activity demonstrates how an app can integrate its own custom app restriction settings
- * with the restricted profile feature.
- *
- * This sample app maintains custom app restriction settings in shared preferences. When
- * the activity is invoked (from Settings > Users), the stored settings are used to initialize
- * the custom configuration on the user interface. Three sample input types are
- * shown: checkbox, single-choice, and multi-choice. When the settings are modified by the user,
- * the corresponding restriction entries are saved, which are retrievable under a restricted
- * profile.
- */
-public class CustomRestrictionsActivity extends Activity {
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- if (savedInstanceState == null) {
- getFragmentManager().beginTransaction().replace(android.R.id.content,
- new CustomRestrictionsFragment()).commit();
- }
- }
-}
diff --git a/samples/AppRestrictions/src/com/example/android/apprestrictions/CustomRestrictionsFragment.java b/samples/AppRestrictions/src/com/example/android/apprestrictions/CustomRestrictionsFragment.java
deleted file mode 100644
index b04dfd1..0000000
--- a/samples/AppRestrictions/src/com/example/android/apprestrictions/CustomRestrictionsFragment.java
+++ /dev/null
@@ -1,202 +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.apprestrictions;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-import android.content.RestrictionEntry;
-import android.os.Bundle;
-import android.os.UserManager;
-import android.preference.CheckBoxPreference;
-import android.preference.ListPreference;
-import android.preference.MultiSelectListPreference;
-import android.preference.Preference;
-import android.preference.PreferenceFragment;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-/**
- * This fragment is included in {@code CustomRestrictionsActivity}. It demonstrates how an app
- * can integrate its own custom app restriction settings with the restricted profile feature.
- *
- * This sample app maintains custom app restriction settings in shared preferences. Your app
- * can use other methods to maintain the settings. When this activity is invoked
- * (from Settings > Users > Restricted Profile), the shared preferences are used to initialize
- * the custom configuration on the user interface.
- *
- * Three sample input types are shown: checkbox, single-choice, and multi-choice. When the
- * settings are modified by the user, the corresponding restriction entries are saved in the
- * platform. The saved restriction entries are retrievable when the app is launched under a
- * restricted profile.
- */
-public class CustomRestrictionsFragment extends PreferenceFragment
- implements Preference.OnPreferenceChangeListener {
-
- // Shared preference key for the boolean restriction.
- private static final String KEY_BOOLEAN_PREF = "pref_boolean";
- // Shared preference key for the single-select restriction.
- private static final String KEY_CHOICE_PREF = "pref_choice";
- // Shared preference key for the multi-select restriction.
- private static final String KEY_MULTI_PREF = "pref_multi";
-
-
- private List<RestrictionEntry> mRestrictions;
- private Bundle mRestrictionsBundle;
-
- // Shared preferences for each of the sample input types.
- private CheckBoxPreference mBooleanPref;
- private ListPreference mChoicePref;
- private MultiSelectListPreference mMultiPref;
-
- // Restriction entries for each of the sample input types.
- private RestrictionEntry mBooleanEntry;
- private RestrictionEntry mChoiceEntry;
- private RestrictionEntry mMultiEntry;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- addPreferencesFromResource(R.xml.custom_prefs);
-
- // This sample app uses shared preferences to maintain app restriction settings. Your app
- // can use other methods to maintain the settings.
- mBooleanPref = (CheckBoxPreference) findPreference(KEY_BOOLEAN_PREF);
- mChoicePref = (ListPreference) findPreference(KEY_CHOICE_PREF);
- mMultiPref = (MultiSelectListPreference) findPreference(KEY_MULTI_PREF);
-
- mBooleanPref.setOnPreferenceChangeListener(this);
- mChoicePref.setOnPreferenceChangeListener(this);
- mMultiPref.setOnPreferenceChangeListener(this);
-
- setRetainInstance(true);
- }
-
- @Override
- public void onActivityCreated(Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
- final Activity activity = getActivity();
-
- // BEGIN_INCLUDE (GET_CURRENT_RESTRICTIONS)
- // Existing app restriction settings, if exist, can be retrieved from the Bundle.
- mRestrictionsBundle =
- activity.getIntent().getBundleExtra(Intent.EXTRA_RESTRICTIONS_BUNDLE);
-
- if (mRestrictionsBundle == null) {
- mRestrictionsBundle =
- ((UserManager) activity.getSystemService(Context.USER_SERVICE))
- .getApplicationRestrictions(activity.getPackageName());
- }
-
- if (mRestrictionsBundle == null) {
- mRestrictionsBundle = new Bundle();
- }
-
- mRestrictions = activity.getIntent().getParcelableArrayListExtra(
- Intent.EXTRA_RESTRICTIONS_LIST);
- // END_INCLUDE (GET_CURRENT_RESTRICTIONS)
-
- // Transfers the saved values into the preference hierarchy.
- if (mRestrictions != null) {
- for (RestrictionEntry entry : mRestrictions) {
- if (entry.getKey().equals(GetRestrictionsReceiver.KEY_BOOLEAN)) {
- mBooleanPref.setChecked(entry.getSelectedState());
- mBooleanEntry = entry;
- } else if (entry.getKey().equals(GetRestrictionsReceiver.KEY_CHOICE)) {
- mChoicePref.setValue(entry.getSelectedString());
- mChoiceEntry = entry;
- } else if (entry.getKey().equals(GetRestrictionsReceiver.KEY_MULTI_SELECT)) {
- HashSet<String> set = new HashSet<String>();
- for (String value : entry.getAllSelectedStrings()) {
- set.add(value);
- }
- mMultiPref.setValues(set);
- mMultiEntry = entry;
- }
- }
- } else {
- mRestrictions = new ArrayList<RestrictionEntry>();
-
- // Initializes the boolean restriction entry and updates its corresponding shared
- // preference value.
- mBooleanEntry = new RestrictionEntry(GetRestrictionsReceiver.KEY_BOOLEAN,
- mRestrictionsBundle.getBoolean(GetRestrictionsReceiver.KEY_BOOLEAN, false));
- mBooleanEntry.setType(RestrictionEntry.TYPE_BOOLEAN);
- mBooleanPref.setChecked(mBooleanEntry.getSelectedState());
-
- // Initializes the single choice restriction entry and updates its corresponding
- // shared preference value.
- mChoiceEntry = new RestrictionEntry(GetRestrictionsReceiver.KEY_CHOICE,
- mRestrictionsBundle.getString(GetRestrictionsReceiver.KEY_CHOICE));
- mChoiceEntry.setType(RestrictionEntry.TYPE_CHOICE);
- mChoicePref.setValue(mChoiceEntry.getSelectedString());
-
- // Initializes the multi-select restriction entry and updates its corresponding
- // shared preference value.
- mMultiEntry = new RestrictionEntry(GetRestrictionsReceiver.KEY_MULTI_SELECT,
- mRestrictionsBundle.getStringArray(
- GetRestrictionsReceiver.KEY_MULTI_SELECT));
- mMultiEntry.setType(RestrictionEntry.TYPE_MULTI_SELECT);
- if (mMultiEntry.getAllSelectedStrings() != null) {
- HashSet<String> set = new HashSet<String>();
- final String[] values = mRestrictionsBundle.getStringArray(
- GetRestrictionsReceiver.KEY_MULTI_SELECT);
- if (values != null) {
- for (String value : values) {
- set.add(value);
- }
- }
- mMultiPref.setValues(set);
- }
- mRestrictions.add(mBooleanEntry);
- mRestrictions.add(mChoiceEntry);
- mRestrictions.add(mMultiEntry);
- }
- // Prepares result to be passed back to the Settings app when the custom restrictions
- // activity finishes.
- Intent intent = new Intent(getActivity().getIntent());
- intent.putParcelableArrayListExtra(Intent.EXTRA_RESTRICTIONS_LIST,
- new ArrayList<RestrictionEntry>(mRestrictions));
- getActivity().setResult(Activity.RESULT_OK, intent);
- }
-
- @Override
- public boolean onPreferenceChange(Preference preference, Object newValue) {
- if (preference == mBooleanPref) {
- mBooleanEntry.setSelectedState((Boolean) newValue);
- } else if (preference == mChoicePref) {
- mChoiceEntry.setSelectedString((String) newValue);
- } else if (preference == mMultiPref) {
- String[] selectedStrings = new String[((Set<String>)newValue).size()];
- int i = 0;
- for (String value : (Set<String>) newValue) {
- selectedStrings[i++] = value;
- }
- mMultiEntry.setAllSelectedStrings(selectedStrings);
- }
-
- // Saves all the app restriction configuration changes from the custom activity.
- Intent intent = new Intent(getActivity().getIntent());
- intent.putParcelableArrayListExtra(Intent.EXTRA_RESTRICTIONS_LIST,
- new ArrayList<RestrictionEntry>(mRestrictions));
- getActivity().setResult(Activity.RESULT_OK, intent);
- return true;
- }
-}
diff --git a/samples/AppRestrictions/src/com/example/android/apprestrictions/GetRestrictionsReceiver.java b/samples/AppRestrictions/src/com/example/android/apprestrictions/GetRestrictionsReceiver.java
deleted file mode 100644
index bb5a283..0000000
--- a/samples/AppRestrictions/src/com/example/android/apprestrictions/GetRestrictionsReceiver.java
+++ /dev/null
@@ -1,162 +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.apprestrictions;
-
-import android.app.Activity;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.RestrictionEntry;
-import android.content.res.Resources;
-import android.os.Bundle;
-import android.preference.PreferenceManager;
-import android.util.Log;
-
-import java.util.ArrayList;
-
-public class GetRestrictionsReceiver extends BroadcastReceiver {
- private static final String TAG = GetRestrictionsReceiver.class.getSimpleName();
-
- // Keys for referencing app restriction settings from the platform.
- public static final String KEY_BOOLEAN = "boolean_key";
- public static final String KEY_CHOICE = "choice_key";
- public static final String KEY_MULTI_SELECT = "multi_key";
-
- @Override
- public void onReceive(final Context context, Intent intent) {
- final PendingResult result = goAsync();
-
- // If app restriction settings are already created, they will be included in the Bundle
- // as key/value pairs.
- final Bundle existingRestrictions =
- intent.getBundleExtra(Intent.EXTRA_RESTRICTIONS_BUNDLE);
- Log.i(TAG, "existingRestrictions = " + existingRestrictions);
-
- new Thread() {
- public void run() {
- createRestrictions(context, result, existingRestrictions);
- }
- }.start();
- }
-
- // Initializes a boolean type restriction entry.
- public static void populateBooleanEntry(Resources res, RestrictionEntry entry) {
- entry.setType(RestrictionEntry.TYPE_BOOLEAN);
- entry.setTitle(res.getString(R.string.boolean_entry_title));
- }
-
- // Initializes a single choice type restriction entry.
- public static void populateChoiceEntry(Resources res, RestrictionEntry reSingleChoice) {
- String[] choiceEntries = res.getStringArray(R.array.choice_entry_entries);
- String[] choiceValues = res.getStringArray(R.array.choice_entry_values);
- if (reSingleChoice.getSelectedString() == null) {
- reSingleChoice.setSelectedString(choiceValues[0]);
- }
- reSingleChoice.setTitle(res.getString(R.string.choice_entry_title));
- reSingleChoice.setChoiceEntries(choiceEntries);
- reSingleChoice.setChoiceValues(choiceValues);
- reSingleChoice.setType(RestrictionEntry.TYPE_CHOICE);
- }
-
- // Initializes a multi-select type restriction entry.
- public static void populateMultiEntry(Resources res, RestrictionEntry reMultiSelect) {
- String[] multiEntries = res.getStringArray(R.array.multi_entry_entries);
- String[] multiValues = res.getStringArray(R.array.multi_entry_values);
- if (reMultiSelect.getAllSelectedStrings() == null) {
- reMultiSelect.setAllSelectedStrings(new String[0]);
- }
- reMultiSelect.setTitle(res.getString(R.string.multi_entry_title));
- reMultiSelect.setChoiceEntries(multiEntries);
- reMultiSelect.setChoiceValues(multiValues);
- reMultiSelect.setType(RestrictionEntry.TYPE_MULTI_SELECT);
- }
-
- // Demonstrates the creation of standard app restriction types: boolean, single choice, and
- // multi-select.
- private ArrayList<RestrictionEntry> initRestrictions(Context context) {
- ArrayList<RestrictionEntry> newRestrictions = new ArrayList<RestrictionEntry>();
- Resources res = context.getResources();
-
- RestrictionEntry reBoolean = new RestrictionEntry(KEY_BOOLEAN, false);
- populateBooleanEntry(res, reBoolean);
- newRestrictions.add(reBoolean);
-
- RestrictionEntry reSingleChoice = new RestrictionEntry(KEY_CHOICE, (String) null);
- populateChoiceEntry(res, reSingleChoice);
- newRestrictions.add(reSingleChoice);
-
- RestrictionEntry reMultiSelect = new RestrictionEntry(KEY_MULTI_SELECT, (String[]) null);
- populateMultiEntry(res, reMultiSelect);
- newRestrictions.add(reMultiSelect);
-
- return newRestrictions;
- }
-
- private void createRestrictions(Context context, PendingResult result,
- Bundle existingRestrictions) {
- // The incoming restrictions bundle contains key/value pairs representing existing app
- // restrictions for this package. In order to retain existing app restrictions, you need to
- // construct new restriction entries and then copy in any existing values for the new keys.
- ArrayList<RestrictionEntry> newEntries = initRestrictions(context);
-
- // If app restrictions were not previously configured for the package, create the default
- // restrictions entries and return them.
- if (existingRestrictions == null) {
- Bundle extras = new Bundle();
- extras.putParcelableArrayList(Intent.EXTRA_RESTRICTIONS_LIST, newEntries);
- result.setResult(Activity.RESULT_OK, null, extras);
- result.finish();
- return;
- }
-
- // Retains current restriction settings by transferring existing restriction entries to
- // new ones.
- for (RestrictionEntry entry : newEntries) {
- final String key = entry.getKey();
- if (KEY_BOOLEAN.equals(key)) {
- entry.setSelectedState(existingRestrictions.getBoolean(KEY_BOOLEAN));
- } else if (KEY_CHOICE.equals(key)) {
- if (existingRestrictions.containsKey(KEY_CHOICE)) {
- entry.setSelectedString(existingRestrictions.getString(KEY_CHOICE));
- }
- } else if (KEY_MULTI_SELECT.equals(key)) {
- if (existingRestrictions.containsKey(KEY_MULTI_SELECT)) {
- entry.setAllSelectedStrings(existingRestrictions.getStringArray(key));
- }
- }
- }
-
- final Bundle extras = new Bundle();
-
- // This path demonstrates the use of a custom app restriction activity instead of standard
- // types. When a custom activity is set, the standard types will not be available under
- // app restriction settings.
- //
- // If your app has an existing activity for app restriction configuration, you can set it
- // up with the intent here.
- if (PreferenceManager.getDefaultSharedPreferences(context)
- .getBoolean(MainActivity.CUSTOM_CONFIG_KEY, false)) {
- final Intent customIntent = new Intent();
- customIntent.setClass(context, CustomRestrictionsActivity.class);
- extras.putParcelable(Intent.EXTRA_RESTRICTIONS_INTENT, customIntent);
- }
-
- extras.putParcelableArrayList(Intent.EXTRA_RESTRICTIONS_LIST, newEntries);
- result.setResult(Activity.RESULT_OK, null, extras);
- result.finish();
- }
-}
diff --git a/samples/AppRestrictions/src/com/example/android/apprestrictions/MainActivity.java b/samples/AppRestrictions/src/com/example/android/apprestrictions/MainActivity.java
deleted file mode 100644
index 57c4439..0000000
--- a/samples/AppRestrictions/src/com/example/android/apprestrictions/MainActivity.java
+++ /dev/null
@@ -1,131 +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.apprestrictions;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.os.Bundle;
-import android.os.UserManager;
-import android.preference.PreferenceManager;
-import android.view.View;
-import android.widget.CheckBox;
-import android.widget.TextView;
-
-/**
- * This is the main user interface of the App Restrictions sample app. It demonstrates the use
- * of the App Restriction feature, which is available on Android 4.3 and above tablet devices
- * with the multiuser feature.
- *
- * When launched under the primary User account, you can toggle between standard app restriction
- * types and custom. When launched under a restricted profile, this activity displays app
- * restriction settings, if available.
- *
- * Follow these steps to exercise the feature:
- * 1. If this is the primary user, go to Settings > Users.
- * 2. Create a restricted profile, if one doesn't exist already.
- * 3. Open the profile settings, locate the sample app, and tap the app restriction settings
- * icon. Configure app restrictions for the app.
- * 4. In the lock screen, switch to the user's restricted profile, launch this sample app,
- * and see the configured app restrictions displayed.
- */
-public class MainActivity extends Activity {
- private Bundle mRestrictionsBundle;
-
- // Checkbox to indicate whether custom or standard app restriction types are selected.
- private CheckBox mCustomConfig;
-
- public static final String CUSTOM_CONFIG_KEY = "custom_config";
-
- private TextView mMultiEntryValue;
- private TextView mChoiceEntryValue;
- private TextView mBooleanEntryValue;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- // Sets up user interface elements.
- setContentView(R.layout.main);
-
- mCustomConfig = (CheckBox) findViewById(R.id.custom_app_limits);
- final boolean customChecked =
- PreferenceManager.getDefaultSharedPreferences(this).getBoolean(
- CUSTOM_CONFIG_KEY, false);
- if (customChecked) mCustomConfig.setChecked(true);
-
- mMultiEntryValue = (TextView) findViewById(R.id.multi_entry_id);
- mChoiceEntryValue = (TextView) findViewById(R.id.choice_entry_id);
- mBooleanEntryValue = (TextView) findViewById(R.id.boolean_entry_id);
- }
-
- @Override
- protected void onResume() {
- super.onResume();
-
- // If app restrictions are set for this package, when launched from a restricted profile,
- // the settings are available in the returned Bundle as key/value pairs.
- mRestrictionsBundle =
- ((UserManager) getSystemService(Context.USER_SERVICE))
- .getApplicationRestrictions(getPackageName());
- if (mRestrictionsBundle == null) {
- mRestrictionsBundle = new Bundle();
- }
-
- // Reads and displays values from a boolean type restriction entry, if available.
- // An app can utilize these settings to restrict its content under a restricted profile.
- final String booleanRestrictionValue =
- mRestrictionsBundle.containsKey(GetRestrictionsReceiver.KEY_BOOLEAN) ?
- mRestrictionsBundle.getBoolean(GetRestrictionsReceiver.KEY_BOOLEAN) + "":
- getString(R.string.na);
- mBooleanEntryValue.setText(booleanRestrictionValue);
-
- // Reads and displays values from a single choice restriction entry, if available.
- final String singleChoiceRestrictionValue =
- mRestrictionsBundle.containsKey(GetRestrictionsReceiver.KEY_CHOICE) ?
- mRestrictionsBundle.getString(GetRestrictionsReceiver.KEY_CHOICE) :
- getString(R.string.na);
- mChoiceEntryValue.setText(singleChoiceRestrictionValue);
-
- // Reads and displays values from a multi-select restriction entry, if available.
- final String[] multiSelectValues =
- mRestrictionsBundle.getStringArray(GetRestrictionsReceiver.KEY_MULTI_SELECT);
- if (multiSelectValues == null || multiSelectValues.length == 0) {
- mMultiEntryValue.setText(getString(R.string.na));
- } else {
- String tempValue = "";
- for (String value : multiSelectValues) {
- tempValue = tempValue + value + " ";
- }
- mMultiEntryValue.setText(tempValue);
- }
- }
-
- /**
- * Saves custom app restriction to the shared preference.
- *
- * This flag is used by {@code GetRestrictionsReceiver} to determine if a custom app
- * restriction activity should be used.
- *
- * @param view
- */
- public void onCustomClicked(View view) {
- final SharedPreferences.Editor editor =
- PreferenceManager.getDefaultSharedPreferences(this).edit();
- editor.putBoolean(CUSTOM_CONFIG_KEY, mCustomConfig.isChecked()).commit();
- }
-}
diff --git a/samples/BluetoothLeGatt/Android.mk b/samples/BluetoothLeGatt/Android.mk
deleted file mode 100644
index 1e30b22..0000000
--- a/samples/BluetoothLeGatt/Android.mk
+++ /dev/null
@@ -1,16 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := samples
-
-# Only compile source java files in this apk.
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_PACKAGE_NAME := BluetoothLeGatt
-
-LOCAL_SDK_VERSION := current
-
-include $(BUILD_PACKAGE)
-
-# Use the following include to make our test apk.
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/samples/BluetoothLeGatt/AndroidManifest.xml b/samples/BluetoothLeGatt/AndroidManifest.xml
deleted file mode 100644
index 019d258..0000000
--- a/samples/BluetoothLeGatt/AndroidManifest.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-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.
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.example.bluetooth.le"
- android:versionCode="1"
- android:versionName="1.0">
- <uses-sdk android:minSdkVersion="18"
- android:targetSdkVersion="18"/>
- <!-- Declare this required feature if you want to make the app available to BLE-capable
- devices only. If you want to make your app available to devices that don't support BLE,
- you should omit this in the manifest. Instead, determine BLE capability by using
- PackageManager.hasSystemFeature(FEATURE_BLUETOOTH_LE) -->
- <uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
-
- <uses-permission android:name="android.permission.BLUETOOTH"/>
- <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
-
- <application android:label="@string/app_name"
- android:icon="@drawable/ic_launcher"
- android:theme="@android:style/Theme.Holo.Light">
- <activity android:name=".DeviceScanActivity"
- android:label="@string/app_name">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER"/>
- </intent-filter>
- </activity>
- <activity android:name=".DeviceControlActivity"/>
- <service android:name=".BluetoothLeService" android:enabled="true"/>
- </application>
-</manifest>
diff --git a/samples/BluetoothLeGatt/res/drawable-hdpi/ic_launcher.png b/samples/BluetoothLeGatt/res/drawable-hdpi/ic_launcher.png
deleted file mode 100755
index 15367c0..0000000
--- a/samples/BluetoothLeGatt/res/drawable-hdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/samples/BluetoothLeGatt/res/drawable-mdpi/ic_launcher.png b/samples/BluetoothLeGatt/res/drawable-mdpi/ic_launcher.png
deleted file mode 100755
index ba810a7..0000000
--- a/samples/BluetoothLeGatt/res/drawable-mdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/samples/BluetoothLeGatt/res/drawable-xhdpi/ic_launcher.png b/samples/BluetoothLeGatt/res/drawable-xhdpi/ic_launcher.png
deleted file mode 100755
index 14f1d74..0000000
--- a/samples/BluetoothLeGatt/res/drawable-xhdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/samples/BluetoothLeGatt/res/drawable-xxhdpi/ic_launcher.png b/samples/BluetoothLeGatt/res/drawable-xxhdpi/ic_launcher.png
deleted file mode 100755
index 81ff9cc..0000000
--- a/samples/BluetoothLeGatt/res/drawable-xxhdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/samples/BluetoothLeGatt/res/layout/actionbar_indeterminate_progress.xml b/samples/BluetoothLeGatt/res/layout/actionbar_indeterminate_progress.xml
deleted file mode 100644
index a950833..0000000
--- a/samples/BluetoothLeGatt/res/layout/actionbar_indeterminate_progress.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<!--
- Copyright 2013 Google Inc.
-
- 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.
- -->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_height="wrap_content"
- android:layout_width="56dp"
- android:minWidth="56dp">
- <ProgressBar android:layout_width="32dp"
- android:layout_height="32dp"
- android:layout_gravity="center"/>
-</FrameLayout>
diff --git a/samples/BluetoothLeGatt/res/layout/gatt_services_characteristics.xml b/samples/BluetoothLeGatt/res/layout/gatt_services_characteristics.xml
deleted file mode 100644
index 2f31061..0000000
--- a/samples/BluetoothLeGatt/res/layout/gatt_services_characteristics.xml
+++ /dev/null
@@ -1,71 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_margin="10dp">
- <LinearLayout android:orientation="horizontal"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_margin="10dp">
- <TextView android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/label_device_address"
- android:textSize="18sp"/>
- <Space android:layout_width="5dp"
- android:layout_height="wrap_content"/>
- <TextView android:id="@+id/device_address"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textSize="18sp"/>
- </LinearLayout>
- <LinearLayout android:orientation="horizontal"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_margin="10dp">
- <TextView android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/label_state"
- android:textSize="18sp"/>
- <Space android:layout_width="5dp"
- android:layout_height="wrap_content"/>
- <TextView android:id="@+id/connection_state"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/disconnected"
- android:textSize="18sp"/>
- </LinearLayout>
- <LinearLayout android:orientation="horizontal"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_margin="10dp">
- <TextView android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/label_data"
- android:textSize="18sp"/>
- <Space android:layout_width="5dp"
- android:layout_height="wrap_content"/>
- <TextView android:id="@+id/data_value"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/no_data"
- android:textSize="18sp"/>
- </LinearLayout>
- <ExpandableListView android:id="@+id/gatt_services_list"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"/>
-</LinearLayout>
\ No newline at end of file
diff --git a/samples/BluetoothLeGatt/res/layout/listitem_device.xml b/samples/BluetoothLeGatt/res/layout/listitem_device.xml
deleted file mode 100644
index eff44fc..0000000
--- a/samples/BluetoothLeGatt/res/layout/listitem_device.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
- <TextView android:id="@+id/device_name"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textSize="24dp"/>
- <TextView android:id="@+id/device_address"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textSize="12dp"/>
-</LinearLayout>
\ No newline at end of file
diff --git a/samples/BluetoothLeGatt/res/menu/gatt_services.xml b/samples/BluetoothLeGatt/res/menu/gatt_services.xml
deleted file mode 100644
index 464d32f..0000000
--- a/samples/BluetoothLeGatt/res/menu/gatt_services.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<menu xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:id="@+id/menu_refresh"
- android:checkable="false"
- android:orderInCategory="1"
- android:showAsAction="ifRoom"/>
- <item android:id="@+id/menu_connect"
- android:title="@string/menu_connect"
- android:orderInCategory="100"
- android:showAsAction="ifRoom|withText"/>
- <item android:id="@+id/menu_disconnect"
- android:title="@string/menu_disconnect"
- android:orderInCategory="101"
- android:showAsAction="ifRoom|withText"/>
-</menu>
diff --git a/samples/BluetoothLeGatt/res/menu/main.xml b/samples/BluetoothLeGatt/res/menu/main.xml
deleted file mode 100644
index 39dd66a..0000000
--- a/samples/BluetoothLeGatt/res/menu/main.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<menu xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:id="@+id/menu_refresh"
- android:checkable="false"
- android:orderInCategory="1"
- android:showAsAction="ifRoom"/>
- <item android:id="@+id/menu_scan"
- android:title="@string/menu_scan"
- android:orderInCategory="100"
- android:showAsAction="ifRoom|withText"/>
- <item android:id="@+id/menu_stop"
- android:title="@string/menu_stop"
- android:orderInCategory="101"
- android:showAsAction="ifRoom|withText"/>
-</menu>
diff --git a/samples/BluetoothLeGatt/res/values/strings.xml b/samples/BluetoothLeGatt/res/values/strings.xml
deleted file mode 100644
index d828aa0..0000000
--- a/samples/BluetoothLeGatt/res/values/strings.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<resources>
- <string name="app_name">BLE Sample</string>
- <string name="ble_not_supported">BLE is not supported</string>
- <string name="label_data">Data:</string>
- <string name="label_device_address">Device address:</string>
- <string name="label_state">State:</string>
- <string name="no_data">No data</string>
- <string name="connected">Connected</string>
- <string name="disconnected">Disconnected</string>
- <string name="title_devices">BLE Device Scan</string>
- <string name="error_bluetooth_not_supported">Bluetooth not supported.</string>
-
- <string name="unknown_device">Unknown device</string>
- <string name="unknown_characteristic">Unknown characteristic</string>
- <string name="unknown_service">Unknown service</string>
-
- <!-- Menu items -->
- <string name="menu_connect">Connect</string>
- <string name="menu_disconnect">Disconnect</string>
- <string name="menu_scan">Scan</string>
- <string name="menu_stop">Stop</string>
-</resources>
diff --git a/samples/BluetoothLeGatt/src/com/example/bluetooth/le/BluetoothLeService.java b/samples/BluetoothLeGatt/src/com/example/bluetooth/le/BluetoothLeService.java
deleted file mode 100644
index 9e7aabd..0000000
--- a/samples/BluetoothLeGatt/src/com/example/bluetooth/le/BluetoothLeService.java
+++ /dev/null
@@ -1,319 +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.bluetooth.le;
-
-import android.app.Service;
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothGatt;
-import android.bluetooth.BluetoothGattCallback;
-import android.bluetooth.BluetoothGattCharacteristic;
-import android.bluetooth.BluetoothGattDescriptor;
-import android.bluetooth.BluetoothGattService;
-import android.bluetooth.BluetoothManager;
-import android.bluetooth.BluetoothProfile;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Binder;
-import android.os.IBinder;
-import android.util.Log;
-
-import java.util.List;
-import java.util.UUID;
-
-/**
- * Service for managing connection and data communication with a GATT server hosted on a
- * given Bluetooth LE device.
- */
-public class BluetoothLeService extends Service {
- private final static String TAG = BluetoothLeService.class.getSimpleName();
-
- private BluetoothManager mBluetoothManager;
- private BluetoothAdapter mBluetoothAdapter;
- private String mBluetoothDeviceAddress;
- private BluetoothGatt mBluetoothGatt;
- private int mConnectionState = STATE_DISCONNECTED;
-
- private static final int STATE_DISCONNECTED = 0;
- private static final int STATE_CONNECTING = 1;
- private static final int STATE_CONNECTED = 2;
-
- public final static String ACTION_GATT_CONNECTED =
- "com.example.bluetooth.le.ACTION_GATT_CONNECTED";
- public final static String ACTION_GATT_DISCONNECTED =
- "com.example.bluetooth.le.ACTION_GATT_DISCONNECTED";
- public final static String ACTION_GATT_SERVICES_DISCOVERED =
- "com.example.bluetooth.le.ACTION_GATT_SERVICES_DISCOVERED";
- public final static String ACTION_DATA_AVAILABLE =
- "com.example.bluetooth.le.ACTION_DATA_AVAILABLE";
- public final static String EXTRA_DATA =
- "com.example.bluetooth.le.EXTRA_DATA";
-
- public final static UUID UUID_HEART_RATE_MEASUREMENT =
- UUID.fromString(SampleGattAttributes.HEART_RATE_MEASUREMENT);
-
- // Implements callback methods for GATT events that the app cares about. For example,
- // connection change and services discovered.
- private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
- @Override
- public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
- String intentAction;
- if (newState == BluetoothProfile.STATE_CONNECTED) {
- intentAction = ACTION_GATT_CONNECTED;
- mConnectionState = STATE_CONNECTED;
- broadcastUpdate(intentAction);
- Log.i(TAG, "Connected to GATT server.");
- // Attempts to discover services after successful connection.
- Log.i(TAG, "Attempting to start service discovery:" +
- mBluetoothGatt.discoverServices());
-
- } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
- intentAction = ACTION_GATT_DISCONNECTED;
- mConnectionState = STATE_DISCONNECTED;
- Log.i(TAG, "Disconnected from GATT server.");
- broadcastUpdate(intentAction);
- }
- }
-
- @Override
- public void onServicesDiscovered(BluetoothGatt gatt, int status) {
- if (status == BluetoothGatt.GATT_SUCCESS) {
- broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
- } else {
- Log.w(TAG, "onServicesDiscovered received: " + status);
- }
- }
-
- @Override
- public void onCharacteristicRead(BluetoothGatt gatt,
- BluetoothGattCharacteristic characteristic,
- int status) {
- if (status == BluetoothGatt.GATT_SUCCESS) {
- broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
- }
- }
-
- @Override
- public void onCharacteristicChanged(BluetoothGatt gatt,
- BluetoothGattCharacteristic characteristic) {
- broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
- }
- };
-
- private void broadcastUpdate(final String action) {
- final Intent intent = new Intent(action);
- sendBroadcast(intent);
- }
-
- private void broadcastUpdate(final String action,
- final BluetoothGattCharacteristic characteristic) {
- final Intent intent = new Intent(action);
-
- // This is special handling for the Heart Rate Measurement profile. Data parsing is
- // carried out as per profile specifications:
- // http://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.heart_rate_measurement.xml
- if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {
- int flag = characteristic.getProperties();
- int format = -1;
- if ((flag & 0x01) != 0) {
- format = BluetoothGattCharacteristic.FORMAT_UINT16;
- Log.d(TAG, "Heart rate format UINT16.");
- } else {
- format = BluetoothGattCharacteristic.FORMAT_UINT8;
- Log.d(TAG, "Heart rate format UINT8.");
- }
- final int heartRate = characteristic.getIntValue(format, 1);
- Log.d(TAG, String.format("Received heart rate: %d", heartRate));
- intent.putExtra(EXTRA_DATA, String.valueOf(heartRate));
- } else {
- // For all other profiles, writes the data formatted in HEX.
- final byte[] data = characteristic.getValue();
- if (data != null && data.length > 0) {
- final StringBuilder stringBuilder = new StringBuilder(data.length);
- for(byte byteChar : data)
- stringBuilder.append(String.format("%02X ", byteChar));
- intent.putExtra(EXTRA_DATA, new String(data) + "\n" + stringBuilder.toString());
- }
- }
- sendBroadcast(intent);
- }
-
- public class LocalBinder extends Binder {
- BluetoothLeService getService() {
- return BluetoothLeService.this;
- }
- }
-
- @Override
- public IBinder onBind(Intent intent) {
- return mBinder;
- }
-
- @Override
- public boolean onUnbind(Intent intent) {
- // After using a given device, you should make sure that BluetoothGatt.close() is called
- // such that resources are cleaned up properly. In this particular example, close() is
- // invoked when the UI is disconnected from the Service.
- close();
- return super.onUnbind(intent);
- }
-
- private final IBinder mBinder = new LocalBinder();
-
- /**
- * Initializes a reference to the local Bluetooth adapter.
- *
- * @return Return true if the initialization is successful.
- */
- public boolean initialize() {
- // For API level 18 and above, get a reference to BluetoothAdapter through
- // BluetoothManager.
- if (mBluetoothManager == null) {
- mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
- if (mBluetoothManager == null) {
- Log.e(TAG, "Unable to initialize BluetoothManager.");
- return false;
- }
- }
-
- mBluetoothAdapter = mBluetoothManager.getAdapter();
- if (mBluetoothAdapter == null) {
- Log.e(TAG, "Unable to obtain a BluetoothAdapter.");
- return false;
- }
-
- return true;
- }
-
- /**
- * Connects to the GATT server hosted on the Bluetooth LE device.
- *
- * @param address The device address of the destination device.
- *
- * @return Return true if the connection is initiated successfully. The connection result
- * is reported asynchronously through the
- * {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}
- * callback.
- */
- public boolean connect(final String address) {
- if (mBluetoothAdapter == null || address == null) {
- Log.w(TAG, "BluetoothAdapter not initialized or unspecified address.");
- return false;
- }
-
- // Previously connected device. Try to reconnect.
- if (mBluetoothDeviceAddress != null && address.equals(mBluetoothDeviceAddress)
- && mBluetoothGatt != null) {
- Log.d(TAG, "Trying to use an existing mBluetoothGatt for connection.");
- if (mBluetoothGatt.connect()) {
- mConnectionState = STATE_CONNECTING;
- return true;
- } else {
- return false;
- }
- }
-
- final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
- if (device == null) {
- Log.w(TAG, "Device not found. Unable to connect.");
- return false;
- }
- // We want to directly connect to the device, so we are setting the autoConnect
- // parameter to false.
- mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
- Log.d(TAG, "Trying to create a new connection.");
- mBluetoothDeviceAddress = address;
- mConnectionState = STATE_CONNECTING;
- return true;
- }
-
- /**
- * Disconnects an existing connection or cancel a pending connection. The disconnection result
- * is reported asynchronously through the
- * {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}
- * callback.
- */
- public void disconnect() {
- if (mBluetoothAdapter == null || mBluetoothGatt == null) {
- Log.w(TAG, "BluetoothAdapter not initialized");
- return;
- }
- mBluetoothGatt.disconnect();
- }
-
- /**
- * After using a given BLE device, the app must call this method to ensure resources are
- * released properly.
- */
- public void close() {
- if (mBluetoothGatt == null) {
- return;
- }
- mBluetoothGatt.close();
- mBluetoothGatt = null;
- }
-
- /**
- * Request a read on a given {@code BluetoothGattCharacteristic}. The read result is reported
- * asynchronously through the {@code BluetoothGattCallback#onCharacteristicRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int)}
- * callback.
- *
- * @param characteristic The characteristic to read from.
- */
- public void readCharacteristic(BluetoothGattCharacteristic characteristic) {
- if (mBluetoothAdapter == null || mBluetoothGatt == null) {
- Log.w(TAG, "BluetoothAdapter not initialized");
- return;
- }
- mBluetoothGatt.readCharacteristic(characteristic);
- }
-
- /**
- * Enables or disables notification on a give characteristic.
- *
- * @param characteristic Characteristic to act on.
- * @param enabled If true, enable notification. False otherwise.
- */
- public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
- boolean enabled) {
- if (mBluetoothAdapter == null || mBluetoothGatt == null) {
- Log.w(TAG, "BluetoothAdapter not initialized");
- return;
- }
- mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
-
- // This is specific to Heart Rate Measurement.
- if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {
- BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
- UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
- descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
- mBluetoothGatt.writeDescriptor(descriptor);
- }
- }
-
- /**
- * Retrieves a list of supported GATT services on the connected device. This should be
- * invoked only after {@code BluetoothGatt#discoverServices()} completes successfully.
- *
- * @return A {@code List} of supported services.
- */
- public List<BluetoothGattService> getSupportedGattServices() {
- if (mBluetoothGatt == null) return null;
-
- return mBluetoothGatt.getServices();
- }
-}
diff --git a/samples/BluetoothLeGatt/src/com/example/bluetooth/le/DeviceControlActivity.java b/samples/BluetoothLeGatt/src/com/example/bluetooth/le/DeviceControlActivity.java
deleted file mode 100644
index 06b3bb4..0000000
--- a/samples/BluetoothLeGatt/src/com/example/bluetooth/le/DeviceControlActivity.java
+++ /dev/null
@@ -1,309 +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.bluetooth.le;
-
-import android.app.Activity;
-import android.bluetooth.BluetoothGattCharacteristic;
-import android.bluetooth.BluetoothGattService;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.ServiceConnection;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.util.Log;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.widget.ExpandableListView;
-import android.widget.SimpleExpandableListAdapter;
-import android.widget.TextView;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-
-/**
- * For a given BLE device, this Activity provides the user interface to connect, display data,
- * and display GATT services and characteristics supported by the device. The Activity
- * communicates with {@code BluetoothLeService}, which in turn interacts with the
- * Bluetooth LE API.
- */
-public class DeviceControlActivity extends Activity {
- private final static String TAG = DeviceControlActivity.class.getSimpleName();
-
- public static final String EXTRAS_DEVICE_NAME = "DEVICE_NAME";
- public static final String EXTRAS_DEVICE_ADDRESS = "DEVICE_ADDRESS";
-
- private TextView mConnectionState;
- private TextView mDataField;
- private String mDeviceName;
- private String mDeviceAddress;
- private ExpandableListView mGattServicesList;
- private BluetoothLeService mBluetoothLeService;
- private ArrayList<ArrayList<BluetoothGattCharacteristic>> mGattCharacteristics =
- new ArrayList<ArrayList<BluetoothGattCharacteristic>>();
- private boolean mConnected = false;
- private BluetoothGattCharacteristic mNotifyCharacteristic;
-
- private final String LIST_NAME = "NAME";
- private final String LIST_UUID = "UUID";
-
- // Code to manage Service lifecycle.
- private final ServiceConnection mServiceConnection = new ServiceConnection() {
-
- @Override
- public void onServiceConnected(ComponentName componentName, IBinder service) {
- mBluetoothLeService = ((BluetoothLeService.LocalBinder) service).getService();
- if (!mBluetoothLeService.initialize()) {
- Log.e(TAG, "Unable to initialize Bluetooth");
- finish();
- }
- // Automatically connects to the device upon successful start-up initialization.
- mBluetoothLeService.connect(mDeviceAddress);
- }
-
- @Override
- public void onServiceDisconnected(ComponentName componentName) {
- mBluetoothLeService = null;
- }
- };
-
- // Handles various events fired by the Service.
- // ACTION_GATT_CONNECTED: connected to a GATT server.
- // ACTION_GATT_DISCONNECTED: disconnected from a GATT server.
- // ACTION_GATT_SERVICES_DISCOVERED: discovered GATT services.
- // ACTION_DATA_AVAILABLE: received data from the device. This can be a result of read
- // or notification operations.
- private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) {
- mConnected = true;
- updateConnectionState(R.string.connected);
- invalidateOptionsMenu();
- } else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) {
- mConnected = false;
- updateConnectionState(R.string.disconnected);
- invalidateOptionsMenu();
- clearUI();
- } else if (BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED.equals(action)) {
- // Show all the supported services and characteristics on the user interface.
- displayGattServices(mBluetoothLeService.getSupportedGattServices());
- } else if (BluetoothLeService.ACTION_DATA_AVAILABLE.equals(action)) {
- displayData(intent.getStringExtra(BluetoothLeService.EXTRA_DATA));
- }
- }
- };
-
- // If a given GATT characteristic is selected, check for supported features. This sample
- // demonstrates 'Read' and 'Notify' features. See
- // http://d.android.com/reference/android/bluetooth/BluetoothGatt.html for the complete
- // list of supported characteristic features.
- private final ExpandableListView.OnChildClickListener servicesListClickListner =
- new ExpandableListView.OnChildClickListener() {
- @Override
- public boolean onChildClick(ExpandableListView parent, View v, int groupPosition,
- int childPosition, long id) {
- if (mGattCharacteristics != null) {
- final BluetoothGattCharacteristic characteristic =
- mGattCharacteristics.get(groupPosition).get(childPosition);
- final int charaProp = characteristic.getProperties();
- if ((charaProp | BluetoothGattCharacteristic.PROPERTY_READ) > 0) {
- // If there is an active notification on a characteristic, clear
- // it first so it doesn't update the data field on the user interface.
- if (mNotifyCharacteristic != null) {
- mBluetoothLeService.setCharacteristicNotification(
- mNotifyCharacteristic, false);
- mNotifyCharacteristic = null;
- }
- mBluetoothLeService.readCharacteristic(characteristic);
- }
- if ((charaProp | BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) {
- mNotifyCharacteristic = characteristic;
- mBluetoothLeService.setCharacteristicNotification(
- characteristic, true);
- }
- return true;
- }
- return false;
- }
- };
-
- private void clearUI() {
- mGattServicesList.setAdapter((SimpleExpandableListAdapter) null);
- mDataField.setText(R.string.no_data);
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.gatt_services_characteristics);
-
- final Intent intent = getIntent();
- mDeviceName = intent.getStringExtra(EXTRAS_DEVICE_NAME);
- mDeviceAddress = intent.getStringExtra(EXTRAS_DEVICE_ADDRESS);
-
- // Sets up UI references.
- ((TextView) findViewById(R.id.device_address)).setText(mDeviceAddress);
- mGattServicesList = (ExpandableListView) findViewById(R.id.gatt_services_list);
- mGattServicesList.setOnChildClickListener(servicesListClickListner);
- mConnectionState = (TextView) findViewById(R.id.connection_state);
- mDataField = (TextView) findViewById(R.id.data_value);
-
- getActionBar().setTitle(mDeviceName);
- getActionBar().setDisplayHomeAsUpEnabled(true);
- Intent gattServiceIntent = new Intent(this, BluetoothLeService.class);
- bindService(gattServiceIntent, mServiceConnection, BIND_AUTO_CREATE);
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- registerReceiver(mGattUpdateReceiver, makeGattUpdateIntentFilter());
- if (mBluetoothLeService != null) {
- final boolean result = mBluetoothLeService.connect(mDeviceAddress);
- Log.d(TAG, "Connect request result=" + result);
- }
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- unregisterReceiver(mGattUpdateReceiver);
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- unbindService(mServiceConnection);
- mBluetoothLeService = null;
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.gatt_services, menu);
- if (mConnected) {
- menu.findItem(R.id.menu_connect).setVisible(false);
- menu.findItem(R.id.menu_disconnect).setVisible(true);
- } else {
- menu.findItem(R.id.menu_connect).setVisible(true);
- menu.findItem(R.id.menu_disconnect).setVisible(false);
- }
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch(item.getItemId()) {
- case R.id.menu_connect:
- mBluetoothLeService.connect(mDeviceAddress);
- return true;
- case R.id.menu_disconnect:
- mBluetoothLeService.disconnect();
- return true;
- case android.R.id.home:
- onBackPressed();
- return true;
- }
- return super.onOptionsItemSelected(item);
- }
-
- private void updateConnectionState(final int resourceId) {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- mConnectionState.setText(resourceId);
- }
- });
- }
-
- private void displayData(String data) {
- if (data != null) {
- mDataField.setText(data);
- }
- }
-
- // Demonstrates how to iterate through the supported GATT Services/Characteristics.
- // In this sample, we populate the data structure that is bound to the ExpandableListView
- // on the UI.
- private void displayGattServices(List<BluetoothGattService> gattServices) {
- if (gattServices == null) return;
- String uuid = null;
- String unknownServiceString = getResources().getString(R.string.unknown_service);
- String unknownCharaString = getResources().getString(R.string.unknown_characteristic);
- ArrayList<HashMap<String, String>> gattServiceData = new ArrayList<HashMap<String, String>>();
- ArrayList<ArrayList<HashMap<String, String>>> gattCharacteristicData
- = new ArrayList<ArrayList<HashMap<String, String>>>();
- mGattCharacteristics = new ArrayList<ArrayList<BluetoothGattCharacteristic>>();
-
- // Loops through available GATT Services.
- for (BluetoothGattService gattService : gattServices) {
- HashMap<String, String> currentServiceData = new HashMap<String, String>();
- uuid = gattService.getUuid().toString();
- currentServiceData.put(
- LIST_NAME, SampleGattAttributes.lookup(uuid, unknownServiceString));
- currentServiceData.put(LIST_UUID, uuid);
- gattServiceData.add(currentServiceData);
-
- ArrayList<HashMap<String, String>> gattCharacteristicGroupData =
- new ArrayList<HashMap<String, String>>();
- List<BluetoothGattCharacteristic> gattCharacteristics =
- gattService.getCharacteristics();
- ArrayList<BluetoothGattCharacteristic> charas =
- new ArrayList<BluetoothGattCharacteristic>();
-
- // Loops through available Characteristics.
- for (BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {
- charas.add(gattCharacteristic);
- HashMap<String, String> currentCharaData = new HashMap<String, String>();
- uuid = gattCharacteristic.getUuid().toString();
- currentCharaData.put(
- LIST_NAME, SampleGattAttributes.lookup(uuid, unknownCharaString));
- currentCharaData.put(LIST_UUID, uuid);
- gattCharacteristicGroupData.add(currentCharaData);
- }
- mGattCharacteristics.add(charas);
- gattCharacteristicData.add(gattCharacteristicGroupData);
- }
-
- SimpleExpandableListAdapter gattServiceAdapter = new SimpleExpandableListAdapter(
- this,
- gattServiceData,
- android.R.layout.simple_expandable_list_item_2,
- new String[] {LIST_NAME, LIST_UUID},
- new int[] { android.R.id.text1, android.R.id.text2 },
- gattCharacteristicData,
- android.R.layout.simple_expandable_list_item_2,
- new String[] {LIST_NAME, LIST_UUID},
- new int[] { android.R.id.text1, android.R.id.text2 }
- );
- mGattServicesList.setAdapter(gattServiceAdapter);
- }
-
- private static IntentFilter makeGattUpdateIntentFilter() {
- final IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(BluetoothLeService.ACTION_GATT_CONNECTED);
- intentFilter.addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED);
- intentFilter.addAction(BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED);
- intentFilter.addAction(BluetoothLeService.ACTION_DATA_AVAILABLE);
- return intentFilter;
- }
-}
diff --git a/samples/BluetoothLeGatt/src/com/example/bluetooth/le/DeviceScanActivity.java b/samples/BluetoothLeGatt/src/com/example/bluetooth/le/DeviceScanActivity.java
deleted file mode 100644
index 1cc954d..0000000
--- a/samples/BluetoothLeGatt/src/com/example/bluetooth/le/DeviceScanActivity.java
+++ /dev/null
@@ -1,269 +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.bluetooth.le;
-
-import android.app.Activity;
-import android.app.ListActivity;
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothManager;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.os.Bundle;
-import android.os.Handler;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-import android.widget.ListView;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import java.util.ArrayList;
-import java.util.UUID;
-
-/**
- * Activity for scanning and displaying available Bluetooth LE devices.
- */
-public class DeviceScanActivity extends ListActivity {
- private LeDeviceListAdapter mLeDeviceListAdapter;
- private BluetoothAdapter mBluetoothAdapter;
- private boolean mScanning;
- private Handler mHandler;
-
- private static final int REQUEST_ENABLE_BT = 1;
- // Stops scanning after 10 seconds.
- private static final long SCAN_PERIOD = 10000;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- getActionBar().setTitle(R.string.title_devices);
- mHandler = new Handler();
-
- // Use this check to determine whether BLE is supported on the device. Then you can
- // selectively disable BLE-related features.
- if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
- Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
- finish();
- }
-
- // Initializes a Bluetooth adapter. For API level 18 and above, get a reference to
- // BluetoothAdapter through BluetoothManager.
- final BluetoothManager bluetoothManager =
- (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
- mBluetoothAdapter = bluetoothManager.getAdapter();
-
- // Checks if Bluetooth is supported on the device.
- if (mBluetoothAdapter == null) {
- Toast.makeText(this, R.string.error_bluetooth_not_supported, Toast.LENGTH_SHORT).show();
- finish();
- return;
- }
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.main, menu);
- if (!mScanning) {
- menu.findItem(R.id.menu_stop).setVisible(false);
- menu.findItem(R.id.menu_scan).setVisible(true);
- menu.findItem(R.id.menu_refresh).setActionView(null);
- } else {
- menu.findItem(R.id.menu_stop).setVisible(true);
- menu.findItem(R.id.menu_scan).setVisible(false);
- menu.findItem(R.id.menu_refresh).setActionView(
- R.layout.actionbar_indeterminate_progress);
- }
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.menu_scan:
- mLeDeviceListAdapter.clear();
- scanLeDevice(true);
- break;
- case R.id.menu_stop:
- scanLeDevice(false);
- break;
- }
- return true;
- }
-
- @Override
- protected void onResume() {
- super.onResume();
-
- // Ensures Bluetooth is enabled on the device. If Bluetooth is not currently enabled,
- // fire an intent to display a dialog asking the user to grant permission to enable it.
- if (!mBluetoothAdapter.isEnabled()) {
- if (!mBluetoothAdapter.isEnabled()) {
- Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
- startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
- }
- }
-
- // Initializes list view adapter.
- mLeDeviceListAdapter = new LeDeviceListAdapter();
- setListAdapter(mLeDeviceListAdapter);
- scanLeDevice(true);
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- // User chose not to enable Bluetooth.
- if (requestCode == REQUEST_ENABLE_BT && resultCode == Activity.RESULT_CANCELED) {
- finish();
- return;
- }
- super.onActivityResult(requestCode, resultCode, data);
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- scanLeDevice(false);
- mLeDeviceListAdapter.clear();
- }
-
- @Override
- protected void onListItemClick(ListView l, View v, int position, long id) {
- final BluetoothDevice device = mLeDeviceListAdapter.getDevice(position);
- if (device == null) return;
- final Intent intent = new Intent(this, DeviceControlActivity.class);
- intent.putExtra(DeviceControlActivity.EXTRAS_DEVICE_NAME, device.getName());
- intent.putExtra(DeviceControlActivity.EXTRAS_DEVICE_ADDRESS, device.getAddress());
- if (mScanning) {
- mBluetoothAdapter.stopLeScan(mLeScanCallback);
- mScanning = false;
- }
- startActivity(intent);
- }
-
- private void scanLeDevice(final boolean enable) {
- if (enable) {
- // Stops scanning after a pre-defined scan period.
- mHandler.postDelayed(new Runnable() {
- @Override
- public void run() {
- mScanning = false;
- mBluetoothAdapter.stopLeScan(mLeScanCallback);
- invalidateOptionsMenu();
- }
- }, SCAN_PERIOD);
-
- mScanning = true;
- mBluetoothAdapter.startLeScan(mLeScanCallback);
- } else {
- mScanning = false;
- mBluetoothAdapter.stopLeScan(mLeScanCallback);
- }
- invalidateOptionsMenu();
- }
-
- // Adapter for holding devices found through scanning.
- private class LeDeviceListAdapter extends BaseAdapter {
- private ArrayList<BluetoothDevice> mLeDevices;
- private LayoutInflater mInflator;
-
- public LeDeviceListAdapter() {
- super();
- mLeDevices = new ArrayList<BluetoothDevice>();
- mInflator = DeviceScanActivity.this.getLayoutInflater();
- }
-
- public void addDevice(BluetoothDevice device) {
- if(!mLeDevices.contains(device)) {
- mLeDevices.add(device);
- }
- }
-
- public BluetoothDevice getDevice(int position) {
- return mLeDevices.get(position);
- }
-
- public void clear() {
- mLeDevices.clear();
- }
-
- @Override
- public int getCount() {
- return mLeDevices.size();
- }
-
- @Override
- public Object getItem(int i) {
- return mLeDevices.get(i);
- }
-
- @Override
- public long getItemId(int i) {
- return i;
- }
-
- @Override
- public View getView(int i, View view, ViewGroup viewGroup) {
- ViewHolder viewHolder;
- // General ListView optimization code.
- if (view == null) {
- view = mInflator.inflate(R.layout.listitem_device, null);
- viewHolder = new ViewHolder();
- viewHolder.deviceAddress = (TextView) view.findViewById(R.id.device_address);
- viewHolder.deviceName = (TextView) view.findViewById(R.id.device_name);
- view.setTag(viewHolder);
- } else {
- viewHolder = (ViewHolder) view.getTag();
- }
-
- BluetoothDevice device = mLeDevices.get(i);
- final String deviceName = device.getName();
- if (deviceName != null && deviceName.length() > 0)
- viewHolder.deviceName.setText(deviceName);
- else
- viewHolder.deviceName.setText(R.string.unknown_device);
- viewHolder.deviceAddress.setText(device.getAddress());
-
- return view;
- }
- }
-
- // Device scan callback.
- private BluetoothAdapter.LeScanCallback mLeScanCallback =
- new BluetoothAdapter.LeScanCallback() {
-
- @Override
- public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- mLeDeviceListAdapter.addDevice(device);
- mLeDeviceListAdapter.notifyDataSetChanged();
- }
- });
- }
- };
-
- static class ViewHolder {
- TextView deviceName;
- TextView deviceAddress;
- }
-}
\ No newline at end of file
diff --git a/samples/BluetoothLeGatt/src/com/example/bluetooth/le/SampleGattAttributes.java b/samples/BluetoothLeGatt/src/com/example/bluetooth/le/SampleGattAttributes.java
deleted file mode 100644
index 255653e..0000000
--- a/samples/BluetoothLeGatt/src/com/example/bluetooth/le/SampleGattAttributes.java
+++ /dev/null
@@ -1,42 +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.bluetooth.le;
-
-import java.util.HashMap;
-
-/**
- * This class includes a small subset of standard GATT attributes for demonstration purposes.
- */
-public class SampleGattAttributes {
- private static HashMap<String, String> attributes = new HashMap();
- public static String HEART_RATE_MEASUREMENT = "00002a37-0000-1000-8000-00805f9b34fb";
- public static String CLIENT_CHARACTERISTIC_CONFIG = "00002902-0000-1000-8000-00805f9b34fb";
-
- static {
- // Sample Services.
- attributes.put("0000180d-0000-1000-8000-00805f9b34fb", "Heart Rate Service");
- attributes.put("0000180a-0000-1000-8000-00805f9b34fb", "Device Information Service");
- // Sample Characteristics.
- attributes.put(HEART_RATE_MEASUREMENT, "Heart Rate Measurement");
- attributes.put("00002a29-0000-1000-8000-00805f9b34fb", "Manufacturer Name String");
- }
-
- public static String lookup(String uuid, String defaultName) {
- String name = attributes.get(uuid);
- return name == null ? defaultName : name;
- }
-}
diff --git a/samples/devbytes/animation/CardFlip/AndroidManifest.xml b/samples/devbytes/animation/CardFlip/AndroidManifest.xml
new file mode 100644
index 0000000..d915f3e
--- /dev/null
+++ b/samples/devbytes/animation/CardFlip/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<!-- 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.android.cardflip"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <uses-sdk android:minSdkVersion="14"
+ android:targetSdkVersion="17"/>
+ <application android:label="@string/app_name" android:icon="@drawable/ic_launcher">
+ <activity android:name=".CardFlip"
+ android:label="@string/app_name">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/samples/devbytes/animation/CardFlip/res/drawable-hdpi/blue.jpg b/samples/devbytes/animation/CardFlip/res/drawable-hdpi/blue.jpg
new file mode 100644
index 0000000..2f7b786
--- /dev/null
+++ b/samples/devbytes/animation/CardFlip/res/drawable-hdpi/blue.jpg
Binary files differ
diff --git a/samples/devbytes/animation/CardFlip/res/drawable-hdpi/ic_launcher.png b/samples/devbytes/animation/CardFlip/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..96a442e
--- /dev/null
+++ b/samples/devbytes/animation/CardFlip/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/animation/CardFlip/res/drawable-hdpi/red.jpg b/samples/devbytes/animation/CardFlip/res/drawable-hdpi/red.jpg
new file mode 100644
index 0000000..f433603
--- /dev/null
+++ b/samples/devbytes/animation/CardFlip/res/drawable-hdpi/red.jpg
Binary files differ
diff --git a/samples/devbytes/animation/CardFlip/res/drawable-ldpi/ic_launcher.png b/samples/devbytes/animation/CardFlip/res/drawable-ldpi/ic_launcher.png
new file mode 100644
index 0000000..9923872
--- /dev/null
+++ b/samples/devbytes/animation/CardFlip/res/drawable-ldpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/animation/CardFlip/res/drawable-mdpi/ic_launcher.png b/samples/devbytes/animation/CardFlip/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..359047d
--- /dev/null
+++ b/samples/devbytes/animation/CardFlip/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/animation/CardFlip/res/drawable-xhdpi/ic_launcher.png b/samples/devbytes/animation/CardFlip/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..71c6d76
--- /dev/null
+++ b/samples/devbytes/animation/CardFlip/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/animation/CardFlip/res/layout/main.xml b/samples/devbytes/animation/CardFlip/res/layout/main.xml
new file mode 100644
index 0000000..ef23d69
--- /dev/null
+++ b/samples/devbytes/animation/CardFlip/res/layout/main.xml
@@ -0,0 +1,19 @@
+<!-- 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.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/main_relative_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+</RelativeLayout>
\ No newline at end of file
diff --git a/samples/devbytes/animation/CardFlip/res/values/integer.xml b/samples/devbytes/animation/CardFlip/res/values/integer.xml
new file mode 100644
index 0000000..2eb363c
--- /dev/null
+++ b/samples/devbytes/animation/CardFlip/res/values/integer.xml
@@ -0,0 +1,20 @@
+<!-- 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.
+-->
+<resources>
+
+ <integer name="vertical_card_magin">30</integer>
+ <integer name="horizontal_card_magin">30</integer>
+
+</resources>
\ No newline at end of file
diff --git a/samples/devbytes/animation/CardFlip/res/values/strings.xml b/samples/devbytes/animation/CardFlip/res/values/strings.xml
new file mode 100644
index 0000000..bd248d1
--- /dev/null
+++ b/samples/devbytes/animation/CardFlip/res/values/strings.xml
@@ -0,0 +1,19 @@
+<!-- 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.
+-->
+<resources>
+
+ <string name="app_name">CardFlip</string>
+
+</resources>
diff --git a/samples/devbytes/animation/CardFlip/src/com/example/android/cardflip/CardFlip.java b/samples/devbytes/animation/CardFlip/src/com/example/android/cardflip/CardFlip.java
new file mode 100644
index 0000000..746afec
--- /dev/null
+++ b/samples/devbytes/animation/CardFlip/src/com/example/android/cardflip/CardFlip.java
@@ -0,0 +1,297 @@
+/*
+ * 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.cardflip;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.app.Activity;
+import android.os.Build;
+import android.os.Bundle;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+import android.view.ViewTreeObserver;
+import android.widget.RelativeLayout;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This application creates 2 stacks of playing cards. Using fling events,
+ * these cards can be flipped from one stack to another where each flip comes with
+ * an associated animation. The cards can be flipped horizontally from left to right
+ * or right to left depending on which stack the animating card currently belongs to.
+ *
+ * This application demonstrates an animation where a stack of cards can either be
+ * be rotated out or back in about their bottom left corner in a counter-clockwise direction.
+ * Rotate out: Down fling on stack of cards
+ * Rotate in: Up fling on stack of cards
+ * Full rotation: Tap on stack of cards
+ *
+ * Note that in this demo touch events are disabled in the middle of any animation so
+ * only one card can be flipped at a time. When the cards are in a rotated-out
+ * state, no new cards can be rotated to or from that stack. These changes were made to
+ * simplify the code for this demo.
+ */
+
+public class CardFlip extends Activity implements CardFlipListener {
+
+ final static int CARD_PILE_OFFSET = 3;
+ final static int STARTING_NUMBER_CARDS = 15;
+ final static int RIGHT_STACK = 0;
+ final static int LEFT_STACK = 1;
+
+ int mCardWidth = 0;
+ int mCardHeight = 0;
+
+ int mVerticalPadding;
+ int mHorizontalPadding;
+
+ boolean mTouchEventsEnabled = true;
+ boolean[] mIsStackEnabled;
+
+ RelativeLayout mLayout;
+
+ List<ArrayList<CardView>> mStackCards;
+
+ GestureDetector gDetector;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+
+ mStackCards = new ArrayList<ArrayList<CardView>>();
+ mStackCards.add(new ArrayList<CardView>());
+ mStackCards.add(new ArrayList<CardView>());
+
+ mIsStackEnabled = new boolean[2];
+ mIsStackEnabled[0] = true;
+ mIsStackEnabled[1] = true;
+
+ mVerticalPadding = getResources().getInteger(R.integer.vertical_card_magin);
+ mHorizontalPadding = getResources().getInteger(R.integer.horizontal_card_magin);
+
+ gDetector = new GestureDetector(this, mGestureListener);
+
+ mLayout = (RelativeLayout)findViewById(R.id.main_relative_layout);
+ ViewTreeObserver observer = mLayout.getViewTreeObserver();
+ observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+ mLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
+ } else {
+ mLayout.getViewTreeObserver().removeGlobalOnLayoutListener(this);
+ }
+
+ mCardHeight = mLayout.getHeight();
+ mCardWidth = mLayout.getWidth() / 2;
+
+ for (int x = 0; x < STARTING_NUMBER_CARDS; x++) {
+ addNewCard(RIGHT_STACK);
+ }
+ }
+ });
+ }
+
+ /**
+ * Adds a new card to the specified stack. Also performs all the necessary layout setup
+ * to place the card in the correct position.
+ */
+ public void addNewCard(int stack) {
+ CardView view = new CardView(this);
+ view.updateTranslation(mStackCards.get(stack).size());
+ view.setCardFlipListener(this);
+ view.setPadding(mHorizontalPadding, mVerticalPadding, mHorizontalPadding, mVerticalPadding);
+
+ RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(mCardWidth,
+ mCardHeight);
+ params.topMargin = 0;
+ params.leftMargin = (stack == RIGHT_STACK ? mCardWidth : 0);
+
+ mStackCards.get(stack).add(view);
+ mLayout.addView(view, params);
+ }
+
+ /**
+ * Gesture Detector listens for fling events in order to potentially initiate
+ * a card flip event when a fling event occurs. Also listens for tap events in
+ * order to potentially initiate a full rotation animation.
+ */
+ private GestureDetector.SimpleOnGestureListener mGestureListener = new GestureDetector
+ .SimpleOnGestureListener() {
+ @Override
+ public boolean onSingleTapUp(MotionEvent motionEvent) {
+ int stack = getStack(motionEvent);
+ rotateCardsFullRotation(stack, CardView.Corner.BOTTOM_LEFT);
+ return true;
+ }
+
+ @Override
+ public boolean onFling(MotionEvent motionEvent, MotionEvent motionEvent2, float v,
+ float v2) {
+ int stack = getStack(motionEvent);
+ ArrayList<CardView> cardStack = mStackCards.get(stack);
+ int size = cardStack.size();
+ if (size > 0) {
+ rotateCardView(cardStack.get(size - 1), stack, v, v2);
+ }
+ return true;
+ }
+ };
+
+ /** Returns the appropriate stack corresponding to the MotionEvent. */
+ public int getStack(MotionEvent ev) {
+ boolean isLeft = ev.getX() <= mCardWidth;
+ return isLeft ? LEFT_STACK : RIGHT_STACK;
+ }
+
+ /**
+ * Uses the stack parameter, along with the velocity values of the fling event
+ * to determine in what direction the card must be flipped. By the same logic, the
+ * new stack that the card belongs to after the animation is also determined
+ * and updated.
+ */
+ public void rotateCardView(final CardView cardView, int stack, float velocityX,
+ float velocityY) {
+
+ boolean xGreaterThanY = Math.abs(velocityX) > Math.abs(velocityY);
+
+ boolean bothStacksEnabled = mIsStackEnabled[RIGHT_STACK] && mIsStackEnabled[LEFT_STACK];
+
+ ArrayList<CardView>leftStack = mStackCards.get(LEFT_STACK);
+ ArrayList<CardView>rightStack = mStackCards.get(RIGHT_STACK);
+
+ switch (stack) {
+ case RIGHT_STACK:
+ if (velocityX < 0 && xGreaterThanY) {
+ if (!bothStacksEnabled) {
+ break;
+ }
+ mLayout.bringChildToFront(cardView);
+ mLayout.requestLayout();
+ rightStack.remove(rightStack.size() - 1);
+ leftStack.add(cardView);
+ cardView.flipRightToLeft(leftStack.size() - 1, (int)velocityX);
+ break;
+ } else if (!xGreaterThanY) {
+ boolean rotateCardsOut = velocityY > 0;
+ rotateCards(RIGHT_STACK, CardView.Corner.BOTTOM_LEFT, rotateCardsOut);
+ }
+ break;
+ case LEFT_STACK:
+ if (velocityX > 0 && xGreaterThanY) {
+ if (!bothStacksEnabled) {
+ break;
+ }
+ mLayout.bringChildToFront(cardView);
+ mLayout.requestLayout();
+ leftStack.remove(leftStack.size() - 1);
+ rightStack.add(cardView);
+ cardView.flipLeftToRight(rightStack.size() - 1, (int)velocityX);
+ break;
+ } else if (!xGreaterThanY) {
+ boolean rotateCardsOut = velocityY > 0;
+ rotateCards(LEFT_STACK, CardView.Corner.BOTTOM_LEFT, rotateCardsOut);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ @Override
+ public void onCardFlipEnd() {
+ mTouchEventsEnabled = true;
+ }
+
+ @Override
+ public void onCardFlipStart() {
+ mTouchEventsEnabled = false;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent me) {
+ if (mTouchEventsEnabled) {
+ return gDetector.onTouchEvent(me);
+ } else {
+ return super.onTouchEvent(me);
+ }
+ }
+
+ /**
+ * Retrieves an animator object for each card in the specified stack that either
+ * rotates it in or out depending on its current state. All of these animations
+ * are then played together.
+ */
+ public void rotateCards (final int stack, CardView.Corner corner,
+ final boolean isRotatingOut) {
+ List<Animator> animations = new ArrayList<Animator>();
+
+ ArrayList <CardView> cards = mStackCards.get(stack);
+
+ for (int i = 0; i < cards.size(); i++) {
+ CardView cardView = cards.get(i);
+ animations.add(cardView.getRotationAnimator(i, corner, isRotatingOut, false));
+ mLayout.bringChildToFront(cardView);
+ }
+ /** All the cards are being brought to the front in order to guarantee that
+ * the cards being rotated in the current stack will overlay the cards in the
+ * other stack. After the z-ordering of all the cards is updated, a layout must
+ * be requested in order to apply the changes made.*/
+ mLayout.requestLayout();
+
+ AnimatorSet set = new AnimatorSet();
+ set.playTogether(animations);
+ set.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mIsStackEnabled[stack] = !isRotatingOut;
+ }
+ });
+ set.start();
+ }
+
+ /**
+ * Retrieves an animator object for each card in the specified stack to complete a
+ * full revolution around one of its corners, and plays all of them together.
+ */
+ public void rotateCardsFullRotation (int stack, CardView.Corner corner) {
+ List<Animator> animations = new ArrayList<Animator>();
+
+ ArrayList <CardView> cards = mStackCards.get(stack);
+ for (int i = 0; i < cards.size(); i++) {
+ CardView cardView = cards.get(i);
+ animations.add(cardView.getFullRotationAnimator(i, corner, false));
+ mLayout.bringChildToFront(cardView);
+ }
+ /** Same reasoning for bringing cards to front as in rotateCards().*/
+ mLayout.requestLayout();
+
+ mTouchEventsEnabled = false;
+ AnimatorSet set = new AnimatorSet();
+ set.playTogether(animations);
+ set.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mTouchEventsEnabled = true;
+ }
+ });
+ set.start();
+ }
+}
\ No newline at end of file
diff --git a/samples/devbytes/animation/CardFlip/src/com/example/android/cardflip/CardFlipListener.java b/samples/devbytes/animation/CardFlip/src/com/example/android/cardflip/CardFlipListener.java
new file mode 100644
index 0000000..0af6941
--- /dev/null
+++ b/samples/devbytes/animation/CardFlip/src/com/example/android/cardflip/CardFlipListener.java
@@ -0,0 +1,27 @@
+/*
+ * 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.cardflip;
+
+/**
+ * This interface is used to prevent flipping multiple cards at the same time.
+ * These callback methods are used to disable and re-enable touches when a card
+ * flip animation begins and ends respectively.
+ * */
+public interface CardFlipListener {
+ public void onCardFlipEnd();
+ public void onCardFlipStart();
+}
diff --git a/samples/devbytes/animation/CardFlip/src/com/example/android/cardflip/CardView.java b/samples/devbytes/animation/CardFlip/src/com/example/android/cardflip/CardView.java
new file mode 100644
index 0000000..9a3ab71
--- /dev/null
+++ b/samples/devbytes/animation/CardFlip/src/com/example/android/cardflip/CardView.java
@@ -0,0 +1,329 @@
+/*
+ * 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.cardflip;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.Keyframe;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Matrix;
+import android.graphics.drawable.BitmapDrawable;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.animation.AccelerateDecelerateInterpolator;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+
+/**
+ * This CardView object is a view which can flip horizontally about its edges,
+ * as well as rotate clockwise or counter-clockwise about any of its corners. In
+ * the middle of a flip animation, this view darkens to imitate a shadow-like effect.
+ *
+ * The key behind the design of this view is the fact that the layout parameters and
+ * the animation properties of this view are updated and reset respectively after
+ * every single animation. Therefore, every consecutive animation that this
+ * view experiences is completely independent of what its prior state was.
+ */
+public class CardView extends ImageView {
+
+ enum Corner {
+ TOP_LEFT,
+ TOP_RIGHT,
+ BOTTOM_LEFT,
+ BOTTOM_RIGHT
+ }
+
+ private final int CAMERA_DISTANCE = 8000;
+ private final int MIN_FLIP_DURATION = 300;
+ private final int VELOCITY_TO_DURATION_CONSTANT = 15;
+ private final int MAX_FLIP_DURATION = 700;
+ private final int ROTATION_PER_CARD = 2;
+ private final int ROTATION_DELAY_PER_CARD = 50;
+ private final int ROTATION_DURATION = 2000;
+ private final int ANTIALIAS_BORDER = 1;
+
+ private BitmapDrawable mFrontBitmapDrawable, mBackBitmapDrawable, mCurrentBitmapDrawable;
+
+ private boolean mIsFrontShowing = true;
+ private boolean mIsHorizontallyFlipped = false;
+
+ private Matrix mHorizontalFlipMatrix;
+
+ private CardFlipListener mCardFlipListener;
+
+ public CardView(Context context) {
+ super(context);
+ init(context);
+ }
+
+ public CardView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init(context);
+ }
+
+ /** Loads the bitmap drawables used for the front and back for this card.*/
+ public void init(Context context) {
+ mHorizontalFlipMatrix = new Matrix();
+
+ setCameraDistance(CAMERA_DISTANCE);
+
+ mFrontBitmapDrawable = bitmapWithBorder((BitmapDrawable)getResources()
+ .getDrawable(R.drawable.red));
+ mBackBitmapDrawable = bitmapWithBorder((BitmapDrawable) getResources()
+ .getDrawable(R.drawable.blue));
+
+ updateDrawableBitmap();
+ }
+
+ /**
+ * Adding a 1 pixel transparent border around the bitmap can be used to
+ * anti-alias the image as it rotates.
+ */
+ private BitmapDrawable bitmapWithBorder(BitmapDrawable bitmapDrawable) {
+ Bitmap bitmapWithBorder = Bitmap.createBitmap(bitmapDrawable.getIntrinsicWidth() +
+ ANTIALIAS_BORDER * 2, bitmapDrawable.getIntrinsicHeight() + ANTIALIAS_BORDER * 2,
+ Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmapWithBorder);
+ canvas.drawBitmap(bitmapDrawable.getBitmap(), ANTIALIAS_BORDER, ANTIALIAS_BORDER, null);
+ return new BitmapDrawable(getResources(), bitmapWithBorder);
+ }
+
+ /** Initiates a horizontal flip from right to left. */
+ public void flipRightToLeft(int numberInPile, int velocity) {
+ setPivotX(0);
+ flipHorizontally(numberInPile, false, velocity);
+ }
+
+ /** Initiates a horizontal flip from left to right. */
+ public void flipLeftToRight(int numberInPile, int velocity) {
+ setPivotX(getWidth());
+ flipHorizontally(numberInPile, true, velocity);
+ }
+
+ /**
+ * Animates a horizontal (about the y-axis) flip of this card.
+ * @param numberInPile Specifies how many cards are underneath this card in the new
+ * pile so as to properly adjust its position offset in the stack.
+ * @param clockwise Specifies whether the horizontal animation is 180 degrees
+ * clockwise or 180 degrees counter clockwise.
+ */
+ public void flipHorizontally (int numberInPile, boolean clockwise, int velocity) {
+ toggleFrontShowing();
+
+ PropertyValuesHolder rotation = PropertyValuesHolder.ofFloat(View.ROTATION_Y,
+ clockwise ? 180 : -180);
+
+ PropertyValuesHolder xOffset = PropertyValuesHolder.ofFloat(View.TRANSLATION_X,
+ numberInPile * CardFlip.CARD_PILE_OFFSET);
+ PropertyValuesHolder yOffset = PropertyValuesHolder.ofFloat(View.TRANSLATION_Y,
+ numberInPile * CardFlip.CARD_PILE_OFFSET);
+
+ ObjectAnimator cardAnimator = ObjectAnimator.ofPropertyValuesHolder(this, rotation,
+ xOffset, yOffset);
+ cardAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator valueAnimator) {
+ if (valueAnimator.getAnimatedFraction() >= 0.5) {
+ updateDrawableBitmap();
+ }
+ }
+ });
+
+ Keyframe shadowKeyFrameStart = Keyframe.ofFloat(0, 0);
+ Keyframe shadowKeyFrameMid = Keyframe.ofFloat(0.5f, 1);
+ Keyframe shadowKeyFrameEnd = Keyframe.ofFloat(1, 0);
+ PropertyValuesHolder shadowPropertyValuesHolder = PropertyValuesHolder.ofKeyframe
+ ("shadow", shadowKeyFrameStart, shadowKeyFrameMid, shadowKeyFrameEnd);
+ ObjectAnimator colorizer = ObjectAnimator.ofPropertyValuesHolder(this,
+ shadowPropertyValuesHolder);
+
+ mCardFlipListener.onCardFlipStart();
+ AnimatorSet set = new AnimatorSet();
+ int duration = MAX_FLIP_DURATION - Math.abs(velocity) / VELOCITY_TO_DURATION_CONSTANT;
+ duration = duration < MIN_FLIP_DURATION ? MIN_FLIP_DURATION : duration;
+ set.setDuration(duration);
+ set.playTogether(cardAnimator, colorizer);
+ set.setInterpolator(new AccelerateDecelerateInterpolator());
+ set.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ toggleIsHorizontallyFlipped();
+ updateDrawableBitmap();
+ updateLayoutParams();
+ mCardFlipListener.onCardFlipEnd();
+ }
+ });
+ set.start();
+ }
+
+ /** Darkens this ImageView's image by applying a shadow color filter over it. */
+ public void setShadow(float value) {
+ int colorValue = (int)(255 - 200 * value);
+ setColorFilter(Color.rgb(colorValue, colorValue, colorValue),
+ android.graphics.PorterDuff.Mode.MULTIPLY);
+ }
+
+ public void toggleFrontShowing() {
+ mIsFrontShowing = !mIsFrontShowing;
+ }
+
+ public void toggleIsHorizontallyFlipped() {
+ mIsHorizontallyFlipped = !mIsHorizontallyFlipped;
+ invalidate();
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+ mHorizontalFlipMatrix.setScale(-1, 1, w / 2, h / 2);
+ }
+
+ /**
+ * Scale the canvas horizontally about its midpoint in the case that the card
+ * is in a horizontally flipped state.
+ */
+ @Override
+ protected void onDraw(Canvas canvas) {
+ if (mIsHorizontallyFlipped) {
+ canvas.concat(mHorizontalFlipMatrix);
+ }
+ super.onDraw(canvas);
+ }
+
+ /**
+ * Updates the layout parameters of this view so as to reset the rotationX and
+ * rotationY parameters, and remain independent of its previous position, while
+ * also maintaining its current position in the layout.
+ */
+ public void updateLayoutParams () {
+ RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) getLayoutParams();
+
+ params.leftMargin = (int)(params.leftMargin + ((Math.abs(getRotationY()) % 360) / 180) *
+ (2 * getPivotX () - getWidth()));
+
+ setRotationX(0);
+ setRotationY(0);
+
+ setLayoutParams(params);
+ }
+
+ /**
+ * Toggles the visible bitmap of this view between its front and back drawables
+ * respectively.
+ */
+ public void updateDrawableBitmap () {
+ mCurrentBitmapDrawable = mIsFrontShowing ? mFrontBitmapDrawable : mBackBitmapDrawable;
+ setImageDrawable(mCurrentBitmapDrawable);
+ }
+
+ /**
+ * Sets the appropriate translation of this card depending on how many cards
+ * are in the pile underneath it.
+ */
+ public void updateTranslation (int numInPile) {
+ setTranslationX(CardFlip.CARD_PILE_OFFSET * numInPile);
+ setTranslationY(CardFlip.CARD_PILE_OFFSET * numInPile);
+ }
+
+ /**
+ * Returns a rotation animation which rotates this card by some degree about
+ * one of its corners either in the clockwise or counter-clockwise direction.
+ * Depending on how many cards lie below this one in the stack, this card will
+ * be rotated by a different amount so all the cards are visible when rotated out.
+ */
+ public ObjectAnimator getRotationAnimator (int cardFromTop, Corner corner,
+ boolean isRotatingOut, boolean isClockwise) {
+ rotateCardAroundCorner(corner);
+ int rotation = cardFromTop * ROTATION_PER_CARD;
+
+ if (!isClockwise) {
+ rotation = -rotation;
+ }
+
+ if (!isRotatingOut) {
+ rotation = 0;
+ }
+
+ return ObjectAnimator.ofFloat(this, View.ROTATION, rotation);
+ }
+
+ /**
+ * Returns a full rotation animator which rotates this card by 360 degrees
+ * about one of its corners either in the clockwise or counter-clockwise direction.
+ * Depending on how many cards lie below this one in the stack, a different start
+ * delay is applied to the animation so the cards don't all animate at once.
+ */
+ public ObjectAnimator getFullRotationAnimator (int cardFromTop, Corner corner,
+ boolean isClockwise) {
+ final int currentRotation = (int)getRotation();
+
+ rotateCardAroundCorner(corner);
+ int rotation = 360 - currentRotation;
+ rotation = isClockwise ? rotation : -rotation;
+
+ ObjectAnimator animator = ObjectAnimator.ofFloat(this, View.ROTATION, rotation);
+
+ animator.setStartDelay(ROTATION_DELAY_PER_CARD * cardFromTop);
+ animator.setDuration(ROTATION_DURATION);
+
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ setRotation(currentRotation);
+ }
+ });
+
+ return animator;
+ }
+
+ /**
+ * Sets the appropriate pivot of this card so that it can be rotated about
+ * any one of its four corners.
+ */
+ public void rotateCardAroundCorner(Corner corner) {
+ switch(corner) {
+ case TOP_LEFT:
+ setPivotX(0);
+ setPivotY(0);
+ break;
+ case TOP_RIGHT:
+ setPivotX(getWidth());
+ setPivotY(0);
+ break;
+ case BOTTOM_LEFT:
+ setPivotX(0);
+ setPivotY(getHeight());
+ break;
+ case BOTTOM_RIGHT:
+ setPivotX(getWidth());
+ setPivotY(getHeight());
+ break;
+ }
+ }
+
+ public void setCardFlipListener(CardFlipListener cardFlipListener) {
+ mCardFlipListener = cardFlipListener;
+ }
+
+}
diff --git a/samples/devbytes/animation/ListViewCellInsertion/AndroidManifest.xml b/samples/devbytes/animation/ListViewCellInsertion/AndroidManifest.xml
new file mode 100644
index 0000000..230c2e7
--- /dev/null
+++ b/samples/devbytes/animation/ListViewCellInsertion/AndroidManifest.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.android.insertingcells"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-sdk android:minSdkVersion="14"
+ android:targetSdkVersion="17"/>
+ <application android:label="@string/app_name" android:icon="@drawable/ic_launcher">
+ <activity android:name=".InsertingCells"
+ android:label="@string/app_name">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/samples/devbytes/animation/ListViewCellInsertion/res/drawable-hdpi/border.9.png b/samples/devbytes/animation/ListViewCellInsertion/res/drawable-hdpi/border.9.png
new file mode 100644
index 0000000..f76e008
--- /dev/null
+++ b/samples/devbytes/animation/ListViewCellInsertion/res/drawable-hdpi/border.9.png
Binary files differ
diff --git a/samples/devbytes/animation/ListViewCellInsertion/res/drawable-hdpi/chameleon.jpg b/samples/devbytes/animation/ListViewCellInsertion/res/drawable-hdpi/chameleon.jpg
new file mode 100644
index 0000000..686cc88
--- /dev/null
+++ b/samples/devbytes/animation/ListViewCellInsertion/res/drawable-hdpi/chameleon.jpg
Binary files differ
diff --git a/samples/devbytes/animation/ListViewCellInsertion/res/drawable-hdpi/flower.jpg b/samples/devbytes/animation/ListViewCellInsertion/res/drawable-hdpi/flower.jpg
new file mode 100644
index 0000000..8842483
--- /dev/null
+++ b/samples/devbytes/animation/ListViewCellInsertion/res/drawable-hdpi/flower.jpg
Binary files differ
diff --git a/samples/devbytes/animation/ListViewCellInsertion/res/drawable-hdpi/ic_launcher.png b/samples/devbytes/animation/ListViewCellInsertion/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..96a442e
--- /dev/null
+++ b/samples/devbytes/animation/ListViewCellInsertion/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/animation/ListViewCellInsertion/res/drawable-hdpi/rock.jpg b/samples/devbytes/animation/ListViewCellInsertion/res/drawable-hdpi/rock.jpg
new file mode 100644
index 0000000..8ea0e85
--- /dev/null
+++ b/samples/devbytes/animation/ListViewCellInsertion/res/drawable-hdpi/rock.jpg
Binary files differ
diff --git a/samples/devbytes/animation/ListViewCellInsertion/res/drawable-ldpi/ic_launcher.png b/samples/devbytes/animation/ListViewCellInsertion/res/drawable-ldpi/ic_launcher.png
new file mode 100644
index 0000000..9923872
--- /dev/null
+++ b/samples/devbytes/animation/ListViewCellInsertion/res/drawable-ldpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/animation/ListViewCellInsertion/res/drawable-mdpi/ic_launcher.png b/samples/devbytes/animation/ListViewCellInsertion/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..359047d
--- /dev/null
+++ b/samples/devbytes/animation/ListViewCellInsertion/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/animation/ListViewCellInsertion/res/drawable-xhdpi/ic_launcher.png b/samples/devbytes/animation/ListViewCellInsertion/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..71c6d76
--- /dev/null
+++ b/samples/devbytes/animation/ListViewCellInsertion/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/animation/ListViewCellInsertion/res/layout/activity_main.xml b/samples/devbytes/animation/ListViewCellInsertion/res/layout/activity_main.xml
new file mode 100644
index 0000000..e515309
--- /dev/null
+++ b/samples/devbytes/animation/ListViewCellInsertion/res/layout/activity_main.xml
@@ -0,0 +1,53 @@
+<!-- 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.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/relative_layout"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ tools:context=".MainActivity"
+ android:background="#111">
+
+ <LinearLayout
+ android:id="@+id/linear_layout"
+ android:layout_height="@dimen/cell_height"
+ android:layout_width="match_parent"
+ android:orientation="horizontal">
+
+ <com.example.android.insertingcells.RoundView
+ android:id="@+id/round_view"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"/>
+
+ <Button
+ android:id="@+id/add_row_button"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:text="@string/add_row"
+ android:onClick="addRow"
+ android:layout_weight="2"/>
+
+ </LinearLayout>
+
+ <com.example.android.insertingcells.InsertionListView
+ android:id="@+id/listview"
+ android:layout_below="@id/linear_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+
+</RelativeLayout>
diff --git a/samples/devbytes/animation/ListViewCellInsertion/res/layout/list_view_item.xml b/samples/devbytes/animation/ListViewCellInsertion/res/layout/list_view_item.xml
new file mode 100644
index 0000000..e984b2e
--- /dev/null
+++ b/samples/devbytes/animation/ListViewCellInsertion/res/layout/list_view_item.xml
@@ -0,0 +1,40 @@
+<!-- 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/item_linear_layout"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:orientation="horizontal"
+ android:background="@drawable/border">
+
+ <ImageView
+ android:id="@+id/image_view"
+ android:layout_height="match_parent"
+ android:layout_width="0dp"
+ android:gravity="center_vertical"
+ android:layout_weight="1"
+ android:scaleType="center"/>
+
+ <TextView
+ android:id="@+id/text_view"
+ android:layout_height="fill_parent"
+ android:layout_width="0dp"
+ android:gravity="center"
+ android:layout_weight="2"
+ android:textStyle="bold"
+ android:textSize="22sp"
+ android:textColor="#ffffff"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/samples/devbytes/animation/ListViewCellInsertion/res/values/dimensions.xml b/samples/devbytes/animation/ListViewCellInsertion/res/values/dimensions.xml
new file mode 100644
index 0000000..a18add3
--- /dev/null
+++ b/samples/devbytes/animation/ListViewCellInsertion/res/values/dimensions.xml
@@ -0,0 +1,21 @@
+<!-- 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.
+-->
+
+<resources>
+
+ <dimen name="circle_radius">50dp</dimen>
+ <dimen name="cell_height">150dp</dimen>
+
+</resources>
diff --git a/samples/devbytes/animation/ListViewCellInsertion/res/values/strings.xml b/samples/devbytes/animation/ListViewCellInsertion/res/values/strings.xml
new file mode 100644
index 0000000..cb57d69
--- /dev/null
+++ b/samples/devbytes/animation/ListViewCellInsertion/res/values/strings.xml
@@ -0,0 +1,21 @@
+<!-- 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.
+-->
+
+<resources>
+
+ <string name="app_name">InsertingCells</string>
+ <string name="add_row">Add Row</string>
+
+</resources>
diff --git a/samples/devbytes/animation/ListViewCellInsertion/src/com/example/android/insertingcells/CustomArrayAdapter.java b/samples/devbytes/animation/ListViewCellInsertion/src/com/example/android/insertingcells/CustomArrayAdapter.java
new file mode 100644
index 0000000..870d279
--- /dev/null
+++ b/samples/devbytes/animation/ListViewCellInsertion/src/com/example/android/insertingcells/CustomArrayAdapter.java
@@ -0,0 +1,138 @@
+/*
+ * 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.insertingcells;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PorterDuff.Mode;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * This custom array adapter is used to populate the ListView in this application.
+ * This adapter also maintains a map of unique stable ids for each object in the data set.
+ * Since this adapter has to support the addition of a new cell to the 1ist index, it also
+ * provides a mechanism to add a stable ID for new data that was recently inserted.
+ */
+public class CustomArrayAdapter extends ArrayAdapter<ListItemObject> {
+
+ HashMap<ListItemObject, Integer> mIdMap = new HashMap<ListItemObject, Integer>();
+ List<ListItemObject> mData;
+ Context mContext;
+ int mLayoutViewResourceId;
+ int mCounter;
+
+ public CustomArrayAdapter(Context context, int layoutViewResourceId,
+ List <ListItemObject> data) {
+ super(context, layoutViewResourceId, data);
+ mData = data;
+ mContext = context;
+ mLayoutViewResourceId = layoutViewResourceId;
+ updateStableIds();
+ }
+
+ public long getItemId(int position) {
+ ListItemObject item = getItem(position);
+ if (mIdMap.containsKey(item)) {
+ return mIdMap.get(item);
+ }
+ return -1;
+ }
+
+ public void updateStableIds() {
+ mIdMap.clear();
+ mCounter = 0;
+ for (int i = 0; i < mData.size(); ++i) {
+ mIdMap.put(mData.get(i), mCounter++);
+ }
+ }
+
+ public void addStableIdForDataAtPosition(int position) {
+ mIdMap.put(mData.get(position), ++mCounter);
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+
+ ListItemObject obj = mData.get(position);
+
+ if(convertView == null) {
+ LayoutInflater inflater = ((Activity)mContext).getLayoutInflater();
+ convertView = inflater.inflate(mLayoutViewResourceId, parent, false);
+ }
+
+ convertView.setLayoutParams(new ListView.LayoutParams(ListView.LayoutParams
+ .MATCH_PARENT, obj.getHeight()));
+
+ ImageView imgView = (ImageView)convertView.findViewById(R.id.image_view);
+ TextView textView = (TextView)convertView.findViewById(R.id.text_view);
+
+ Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(),
+ obj.getImgResource(), null);
+
+ textView.setText(obj.getTitle());
+ imgView.setImageBitmap(CustomArrayAdapter.getCroppedBitmap(bitmap));
+
+ return convertView;
+ }
+
+ /**
+ * Returns a circular cropped version of the bitmap passed in.
+ */
+ public static Bitmap getCroppedBitmap(Bitmap bitmap) {
+ Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(),
+ Config.ARGB_8888);
+
+ final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
+
+ Canvas canvas = new Canvas(output);
+
+ final Paint paint = new Paint();
+ paint.setAntiAlias(true);
+
+ int halfWidth = bitmap.getWidth() / 2;
+ int halfHeight = bitmap.getHeight() / 2;
+
+ canvas.drawCircle(halfWidth, halfHeight, Math.max(halfWidth, halfHeight), paint);
+
+ paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
+
+ canvas.drawBitmap(bitmap, rect, rect, paint);
+
+ return output;
+ }
+}
\ No newline at end of file
diff --git a/samples/devbytes/animation/ListViewCellInsertion/src/com/example/android/insertingcells/InsertingCells.java b/samples/devbytes/animation/ListViewCellInsertion/src/com/example/android/insertingcells/InsertingCells.java
new file mode 100644
index 0000000..a58dbfb
--- /dev/null
+++ b/samples/devbytes/animation/ListViewCellInsertion/src/com/example/android/insertingcells/InsertingCells.java
@@ -0,0 +1,113 @@
+/*
+ * 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.insertingcells;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.Button;
+import android.widget.RelativeLayout;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This application creates a ListView to which new elements can be added from the
+ * top. When a new element is added, it is animated from above the bounds
+ * of the list to the top. When the list is scrolled all the way to the top and a new
+ * element is added, the row animation is accompanied by an image animation that pops
+ * out of the round view and pops into the correct position in the top cell.
+ */
+public class InsertingCells extends Activity implements OnRowAdditionAnimationListener {
+
+ private ListItemObject mValues[];
+
+ private InsertionListView mListView;
+
+ private Button mButton;
+
+ private Integer mItemNum = 0;
+
+ private RoundView mRoundView;
+
+ private int mCellHeight;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ mValues = new ListItemObject[] {
+ new ListItemObject("Chameleon", R.drawable.chameleon, 0),
+ new ListItemObject("Rock", R.drawable.rock, 0),
+ new ListItemObject("Flower", R.drawable.flower, 0),
+ };
+
+ mCellHeight = (int)(getResources().getDimension(R.dimen.cell_height));
+
+ List<ListItemObject> mData = new ArrayList<ListItemObject>();
+ CustomArrayAdapter mAdapter = new CustomArrayAdapter(this, R.layout.list_view_item, mData);
+ RelativeLayout mLayout = (RelativeLayout)findViewById(R.id.relative_layout);
+
+ mRoundView = (RoundView)findViewById(R.id.round_view);
+ mButton = (Button)findViewById(R.id.add_row_button);
+ mListView = (InsertionListView)findViewById(R.id.listview);
+
+ mListView.setAdapter(mAdapter);
+ mListView.setData(mData);
+ mListView.setLayout(mLayout);
+ mListView.setRowAdditionAnimationListener(this);
+ }
+
+ public void addRow(View view) {
+ mButton.setEnabled(false);
+
+ mItemNum++;
+ ListItemObject obj = mValues[mItemNum % mValues.length];
+ final ListItemObject newObj = new ListItemObject(obj.getTitle(), obj.getImgResource(),
+ mCellHeight);
+
+ boolean shouldAnimateInNewImage = mListView.shouldAnimateInNewImage();
+ if (!shouldAnimateInNewImage) {
+ mListView.addRow(newObj);
+ return;
+ }
+
+ mListView.setEnabled(false);
+ ObjectAnimator animator = mRoundView.getScalingAnimator();
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+ mListView.addRow(newObj);
+ }
+ });
+ animator.start();
+ }
+
+ @Override
+ public void onRowAdditionAnimationStart() {
+ mButton.setEnabled(false);
+ }
+
+ @Override
+ public void onRowAdditionAnimationEnd() {
+ mButton.setEnabled(true);
+ }
+}
diff --git a/samples/devbytes/animation/ListViewCellInsertion/src/com/example/android/insertingcells/InsertionListView.java b/samples/devbytes/animation/ListViewCellInsertion/src/com/example/android/insertingcells/InsertionListView.java
new file mode 100644
index 0000000..66866ba
--- /dev/null
+++ b/samples/devbytes/animation/ListViewCellInsertion/src/com/example/android/insertingcells/InsertionListView.java
@@ -0,0 +1,376 @@
+/*
+ * 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.insertingcells;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.animation.TypeEvaluator;
+import android.animation.ValueAnimator;
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.view.animation.OvershootInterpolator;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * This ListView displays a set of ListItemObjects. By calling addRow with a new
+ * ListItemObject, it is added to the top of the ListView and the new row is animated
+ * in. If the ListView content is at the top (the scroll offset is 0), the animation of
+ * the new row is accompanied by an extra image animation that pops into place in its
+ * corresponding item in the ListView.
+ */
+public class InsertionListView extends ListView {
+
+ private static final int NEW_ROW_DURATION = 500;
+ private static final int OVERSHOOT_INTERPOLATOR_TENSION = 5;
+
+ private OvershootInterpolator sOvershootInterpolator;
+
+ private RelativeLayout mLayout;
+
+ private Context mContext;
+
+ private OnRowAdditionAnimationListener mRowAdditionAnimationListener;
+
+ private List<ListItemObject> mData;
+ private List<BitmapDrawable> mCellBitmapDrawables;
+
+ public InsertionListView(Context context) {
+ super(context);
+ init(context);
+ }
+
+ public InsertionListView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init(context);
+ }
+
+ public InsertionListView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ init(context);
+ }
+
+ public void init(Context context) {
+ setDivider(null);
+ mContext = context;
+ mCellBitmapDrawables = new ArrayList<BitmapDrawable>();
+ sOvershootInterpolator = new OvershootInterpolator(OVERSHOOT_INTERPOLATOR_TENSION);
+ }
+
+ /**
+ * Modifies the underlying data set and adapter through the addition of the new object
+ * to the first item of the ListView. The new cell is then animated into place from
+ * above the bounds of the ListView.
+ */
+ public void addRow(ListItemObject newObj) {
+
+ final CustomArrayAdapter adapter = (CustomArrayAdapter)getAdapter();
+
+ /**
+ * Stores the starting bounds and the corresponding bitmap drawables of every
+ * cell present in the ListView before the data set change takes place.
+ */
+ final HashMap<Long, Rect> listViewItemBounds = new HashMap<Long, Rect>();
+ final HashMap<Long, BitmapDrawable> listViewItemDrawables = new HashMap<Long,
+ BitmapDrawable>();
+
+ int firstVisiblePosition = getFirstVisiblePosition();
+ for (int i = 0; i < getChildCount(); ++i) {
+ View child = getChildAt(i);
+ int position = firstVisiblePosition + i;
+ long itemID = adapter.getItemId(position);
+ Rect startRect = new Rect(child.getLeft(), child.getTop(), child.getRight(),
+ child.getBottom());
+ listViewItemBounds.put(itemID, startRect);
+ listViewItemDrawables.put(itemID, getBitmapDrawableFromView(child));
+ }
+
+ /** Adds the new object to the data set, thereby modifying the adapter,
+ * as well as adding a stable Id for that specified object.*/
+ mData.add(0, newObj);
+ adapter.addStableIdForDataAtPosition(0);
+ adapter.notifyDataSetChanged();
+
+ final ViewTreeObserver observer = getViewTreeObserver();
+ observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
+ public boolean onPreDraw() {
+ observer.removeOnPreDrawListener(this);
+
+ ArrayList<Animator> animations = new ArrayList<Animator>();
+
+ final View newCell = getChildAt(0);
+ final ImageView imgView = (ImageView)newCell.findViewById(R.id.image_view);
+ final ImageView copyImgView = new ImageView(mContext);
+
+ int firstVisiblePosition = getFirstVisiblePosition();
+ final boolean shouldAnimateInNewRow = shouldAnimateInNewRow();
+ final boolean shouldAnimateInImage = shouldAnimateInNewImage();
+
+ if (shouldAnimateInNewRow) {
+ /** Fades in the text of the first cell. */
+ TextView textView = (TextView)newCell.findViewById(R.id.text_view);
+ ObjectAnimator textAlphaAnimator = ObjectAnimator.ofFloat(textView,
+ View.ALPHA, 0.0f, 1.0f);
+ animations.add(textAlphaAnimator);
+
+ /** Animates in the extra hover view corresponding to the image
+ * in the top row of the ListView. */
+ if (shouldAnimateInImage) {
+
+ int width = imgView.getWidth();
+ int height = imgView.getHeight();
+
+ Point childLoc = getLocationOnScreen(newCell);
+ Point layoutLoc = getLocationOnScreen(mLayout);
+
+ ListItemObject obj = mData.get(0);
+ Bitmap bitmap = CustomArrayAdapter.getCroppedBitmap(BitmapFactory
+ .decodeResource(mContext.getResources(), obj.getImgResource(),
+ null));
+ copyImgView.setImageBitmap(bitmap);
+
+ imgView.setVisibility(View.INVISIBLE);
+
+ copyImgView.setScaleType(ImageView.ScaleType.CENTER);
+
+ ObjectAnimator imgViewTranslation = ObjectAnimator.ofFloat(copyImgView,
+ View.Y, childLoc.y - layoutLoc.y);
+
+ PropertyValuesHolder imgViewScaleY = PropertyValuesHolder.ofFloat
+ (View.SCALE_Y, 0, 1.0f);
+ PropertyValuesHolder imgViewScaleX = PropertyValuesHolder.ofFloat
+ (View.SCALE_X, 0, 1.0f);
+ ObjectAnimator imgViewScaleAnimator = ObjectAnimator
+ .ofPropertyValuesHolder(copyImgView, imgViewScaleX, imgViewScaleY);
+ imgViewScaleAnimator.setInterpolator(sOvershootInterpolator);
+ animations.add(imgViewTranslation);
+ animations.add(imgViewScaleAnimator);
+
+ RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams
+ (width, height);
+
+ mLayout.addView(copyImgView, params);
+ }
+ }
+
+ /** Loops through all the current visible cells in the ListView and animates
+ * all of them into their post layout positions from their original positions.*/
+ for (int i = 0; i < getChildCount(); i++) {
+ View child = getChildAt(i);
+ int position = firstVisiblePosition + i;
+ long itemId = adapter.getItemId(position);
+ Rect startRect = listViewItemBounds.get(itemId);
+ int top = child.getTop();
+ if (startRect != null) {
+ /** If the cell was visible before the data set change and
+ * after the data set change, then animate the cell between
+ * the two positions.*/
+ int startTop = startRect.top;
+ int delta = startTop - top;
+ ObjectAnimator animation = ObjectAnimator.ofFloat(child,
+ View.TRANSLATION_Y, delta, 0);
+ animations.add(animation);
+ } else {
+ /** If the cell was not visible (or present) before the data set
+ * change but is visible after the data set change, then use its
+ * height to determine the delta by which it should be animated.*/
+ int childHeight = child.getHeight() + getDividerHeight();
+ int startTop = top + (i > 0 ? childHeight : -childHeight);
+ int delta = startTop - top;
+ ObjectAnimator animation = ObjectAnimator.ofFloat(child,
+ View.TRANSLATION_Y, delta, 0);
+ animations.add(animation);
+ }
+ listViewItemBounds.remove(itemId);
+ listViewItemDrawables.remove(itemId);
+ }
+
+ /**
+ * Loops through all the cells that were visible before the data set
+ * changed but not after, and keeps track of their corresponding
+ * drawables. The bounds of each drawable are then animated from the
+ * original state to the new one (off the screen). By storing all
+ * the drawables that meet this criteria, they can be redrawn on top
+ * of the ListView via dispatchDraw as they are animating.
+ */
+ for (Long itemId: listViewItemBounds.keySet()) {
+ BitmapDrawable bitmapDrawable = listViewItemDrawables.get(itemId);
+ Rect startBounds = listViewItemBounds.get(itemId);
+ bitmapDrawable.setBounds(startBounds);
+
+ int childHeight = startBounds.bottom - startBounds.top + getDividerHeight();
+ Rect endBounds = new Rect(startBounds);
+ endBounds.offset(0, childHeight);
+
+ ObjectAnimator animation = ObjectAnimator.ofObject(bitmapDrawable,
+ "bounds", sBoundsEvaluator, startBounds, endBounds);
+ animation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ private Rect mLastBound = null;
+ private Rect mCurrentBound = new Rect();
+ @Override
+ public void onAnimationUpdate(ValueAnimator valueAnimator) {
+ Rect bounds = (Rect)valueAnimator.getAnimatedValue();
+ mCurrentBound.set(bounds);
+ if (mLastBound != null) {
+ mCurrentBound.union(mLastBound);
+ }
+ mLastBound = bounds;
+ invalidate(mCurrentBound);
+ }
+ });
+
+ listViewItemBounds.remove(itemId);
+ listViewItemDrawables.remove(itemId);
+
+ mCellBitmapDrawables.add(bitmapDrawable);
+
+ animations.add(animation);
+ }
+
+ /** Animates all the cells from their old position to their new position
+ * at the same time.*/
+ setEnabled(false);
+ mRowAdditionAnimationListener.onRowAdditionAnimationStart();
+ AnimatorSet set = new AnimatorSet();
+ set.setDuration(NEW_ROW_DURATION);
+ set.playTogether(animations);
+ set.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mCellBitmapDrawables.clear();
+ imgView.setVisibility(View.VISIBLE);
+ mLayout.removeView(copyImgView);
+ mRowAdditionAnimationListener.onRowAdditionAnimationEnd();
+ setEnabled(true);
+ invalidate();
+ }
+ });
+ set.start();
+
+ listViewItemBounds.clear();
+ listViewItemDrawables.clear();
+ return true;
+ }
+ });
+ }
+
+ /**
+ * By overriding dispatchDraw, the BitmapDrawables of all the cells that were on the
+ * screen before (but not after) the layout are drawn and animated off the screen.
+ */
+ @Override
+ protected void dispatchDraw (Canvas canvas) {
+ super.dispatchDraw(canvas);
+ if (mCellBitmapDrawables.size() > 0) {
+ for (BitmapDrawable bitmapDrawable: mCellBitmapDrawables) {
+ bitmapDrawable.draw(canvas);
+ }
+ }
+ }
+
+ public boolean shouldAnimateInNewRow() {
+ int firstVisiblePosition = getFirstVisiblePosition();
+ return (firstVisiblePosition == 0);
+ }
+
+ public boolean shouldAnimateInNewImage() {
+ if (getChildCount() == 0) {
+ return true;
+ }
+ boolean shouldAnimateInNewRow = shouldAnimateInNewRow();
+ View topCell = getChildAt(0);
+ return (shouldAnimateInNewRow && topCell.getTop() == 0);
+ }
+
+ /** Returns a bitmap drawable showing a screenshot of the view passed in. */
+ private BitmapDrawable getBitmapDrawableFromView(View v) {
+ Bitmap bitmap = Bitmap.createBitmap(v.getWidth(), v.getHeight(), Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas (bitmap);
+ v.draw(canvas);
+ return new BitmapDrawable(getResources(), bitmap);
+ }
+
+ /**
+ * Returns the absolute x,y coordinates of the view relative to the top left
+ * corner of the phone screen.
+ */
+ public Point getLocationOnScreen(View v) {
+ DisplayMetrics dm = new DisplayMetrics();
+ ((Activity)getContext()).getWindowManager().getDefaultDisplay().getMetrics(dm);
+
+ int[] location = new int[2];
+ v.getLocationOnScreen(location);
+
+ return new Point(location[0], location[1]);
+ }
+
+ /** Setter for the underlying data set controlling the adapter. */
+ public void setData(List<ListItemObject> data) {
+ mData = data;
+ }
+
+ /**
+ * Setter for the parent RelativeLayout of this ListView. A reference to this
+ * ViewGroup is required in order to add the custom animated overlaying bitmap
+ * when adding a new row.
+ */
+ public void setLayout(RelativeLayout layout) {
+ mLayout = layout;
+ }
+
+ public void setRowAdditionAnimationListener(OnRowAdditionAnimationListener
+ rowAdditionAnimationListener) {
+ mRowAdditionAnimationListener = rowAdditionAnimationListener;
+ }
+
+ /**
+ * This TypeEvaluator is used to animate the position of a BitmapDrawable
+ * by updating its bounds.
+ */
+ static final TypeEvaluator<Rect> sBoundsEvaluator = new TypeEvaluator<Rect>() {
+ public Rect evaluate(float fraction, Rect startValue, Rect endValue) {
+ return new Rect(interpolate(startValue.left, endValue.left, fraction),
+ interpolate(startValue.top, endValue.top, fraction),
+ interpolate(startValue.right, endValue.right, fraction),
+ interpolate(startValue.bottom, endValue.bottom, fraction));
+ }
+
+ public int interpolate(int start, int end, float fraction) {
+ return (int)(start + fraction * (end - start));
+ }
+ };
+
+}
diff --git a/samples/devbytes/animation/ListViewCellInsertion/src/com/example/android/insertingcells/ListItemObject.java b/samples/devbytes/animation/ListViewCellInsertion/src/com/example/android/insertingcells/ListItemObject.java
new file mode 100644
index 0000000..8ca56f5
--- /dev/null
+++ b/samples/devbytes/animation/ListViewCellInsertion/src/com/example/android/insertingcells/ListItemObject.java
@@ -0,0 +1,48 @@
+/*
+ * 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.insertingcells;
+
+/**
+ * The data model for every cell in the ListView for this application. This model stores
+ * a title, an image resource and a default cell height for every item in the ListView.
+ */
+public class ListItemObject {
+
+ private String mTitle;
+ private int mImgResource;
+ private int mHeight;
+
+ public ListItemObject(String title, int imgResource, int height) {
+ super();
+ mTitle = title;
+ mImgResource = imgResource;
+ mHeight = height;
+ }
+
+ public String getTitle() {
+ return mTitle;
+ }
+
+ public int getImgResource() {
+ return mImgResource;
+ }
+
+ public int getHeight() {
+ return mHeight;
+ }
+
+}
diff --git a/samples/devbytes/animation/ListViewCellInsertion/src/com/example/android/insertingcells/OnRowAdditionAnimationListener.java b/samples/devbytes/animation/ListViewCellInsertion/src/com/example/android/insertingcells/OnRowAdditionAnimationListener.java
new file mode 100644
index 0000000..f371f3e
--- /dev/null
+++ b/samples/devbytes/animation/ListViewCellInsertion/src/com/example/android/insertingcells/OnRowAdditionAnimationListener.java
@@ -0,0 +1,28 @@
+/*
+ * 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.insertingcells;
+
+/**
+ * This listener is used to determine when the animation of a new row addition
+ * begins and ends. The primary use of this interface is to create a callback
+ * under which certain elements, such as the listview itself, can be disabled
+ * to prevent unpredictable behaviour during the actual cell animation.
+ */
+public interface OnRowAdditionAnimationListener {
+ public void onRowAdditionAnimationStart();
+ public void onRowAdditionAnimationEnd();
+}
diff --git a/samples/devbytes/animation/ListViewCellInsertion/src/com/example/android/insertingcells/RoundView.java b/samples/devbytes/animation/ListViewCellInsertion/src/com/example/android/insertingcells/RoundView.java
new file mode 100644
index 0000000..320c8b9
--- /dev/null
+++ b/samples/devbytes/animation/ListViewCellInsertion/src/com/example/android/insertingcells/RoundView.java
@@ -0,0 +1,85 @@
+/*
+ * 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.insertingcells;
+
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.util.AttributeSet;
+import android.view.View;
+
+/**
+ * This round view draws a circle from which the image pops out of and into
+ * the corresponding cell in the list.
+ */
+public class RoundView extends View {
+
+ private final int STROKE_WIDTH = 6;
+ private final int RADIUS = 20;
+ private final int ANIMATION_DURATION = 300;
+ private final float SCALE_FACTOR = 0.3f;
+
+ private Paint mPaint;
+
+ public RoundView(Context context) {
+ super(context);
+ init();
+ }
+
+ public RoundView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init();
+ }
+
+ public RoundView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ init();
+ }
+
+ private void init() {
+ mPaint = new Paint();
+ mPaint.setAntiAlias(true);
+ mPaint.setColor(Color.WHITE);
+ mPaint.setStyle(Paint.Style.STROKE);
+ mPaint.setStrokeWidth(STROKE_WIDTH);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ canvas.drawCircle(canvas.getWidth() / 2, canvas.getHeight() / 2,
+ RADIUS, mPaint);
+ }
+
+ public ObjectAnimator getScalingAnimator() {
+ PropertyValuesHolder imgViewScaleY = PropertyValuesHolder.ofFloat(View
+ .SCALE_Y, SCALE_FACTOR);
+ PropertyValuesHolder imgViewScaleX = PropertyValuesHolder.ofFloat(View
+ .SCALE_X, SCALE_FACTOR);
+
+ ObjectAnimator imgViewScaleAnimator = ObjectAnimator
+ .ofPropertyValuesHolder(this, imgViewScaleX, imgViewScaleY);
+ imgViewScaleAnimator.setRepeatCount(1);
+ imgViewScaleAnimator.setRepeatMode(ValueAnimator.REVERSE);
+ imgViewScaleAnimator.setDuration(ANIMATION_DURATION);
+
+ return imgViewScaleAnimator;
+ }
+}
diff --git a/samples/devbytes/animation/ListViewExpandingCells/AndroidManifest.xml b/samples/devbytes/animation/ListViewExpandingCells/AndroidManifest.xml
new file mode 100644
index 0000000..1633d01
--- /dev/null
+++ b/samples/devbytes/animation/ListViewExpandingCells/AndroidManifest.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.android.expandingcells"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <uses-sdk android:minSdkVersion="16"
+ android:targetSdkVersion="17"/>
+ <application android:label="@string/app_name" android:icon="@drawable/ic_launcher">
+ <activity android:name=".ExpandingCells"
+ android:label="@string/app_name">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/samples/devbytes/animation/ListViewExpandingCells/res/drawable-hdpi/border.9.png b/samples/devbytes/animation/ListViewExpandingCells/res/drawable-hdpi/border.9.png
new file mode 100644
index 0000000..f76e008
--- /dev/null
+++ b/samples/devbytes/animation/ListViewExpandingCells/res/drawable-hdpi/border.9.png
Binary files differ
diff --git a/samples/devbytes/animation/ListViewExpandingCells/res/drawable-hdpi/chameleon.jpg b/samples/devbytes/animation/ListViewExpandingCells/res/drawable-hdpi/chameleon.jpg
new file mode 100644
index 0000000..686cc88
--- /dev/null
+++ b/samples/devbytes/animation/ListViewExpandingCells/res/drawable-hdpi/chameleon.jpg
Binary files differ
diff --git a/samples/devbytes/animation/ListViewExpandingCells/res/drawable-hdpi/flower.jpg b/samples/devbytes/animation/ListViewExpandingCells/res/drawable-hdpi/flower.jpg
new file mode 100644
index 0000000..8842483
--- /dev/null
+++ b/samples/devbytes/animation/ListViewExpandingCells/res/drawable-hdpi/flower.jpg
Binary files differ
diff --git a/samples/devbytes/animation/ListViewExpandingCells/res/drawable-hdpi/ic_launcher.png b/samples/devbytes/animation/ListViewExpandingCells/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..96a442e
--- /dev/null
+++ b/samples/devbytes/animation/ListViewExpandingCells/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/animation/ListViewExpandingCells/res/drawable-hdpi/rock.jpg b/samples/devbytes/animation/ListViewExpandingCells/res/drawable-hdpi/rock.jpg
new file mode 100644
index 0000000..8ea0e85
--- /dev/null
+++ b/samples/devbytes/animation/ListViewExpandingCells/res/drawable-hdpi/rock.jpg
Binary files differ
diff --git a/samples/devbytes/animation/ListViewExpandingCells/res/drawable-ldpi/ic_launcher.png b/samples/devbytes/animation/ListViewExpandingCells/res/drawable-ldpi/ic_launcher.png
new file mode 100644
index 0000000..9923872
--- /dev/null
+++ b/samples/devbytes/animation/ListViewExpandingCells/res/drawable-ldpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/animation/ListViewExpandingCells/res/drawable-mdpi/ic_launcher.png b/samples/devbytes/animation/ListViewExpandingCells/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..359047d
--- /dev/null
+++ b/samples/devbytes/animation/ListViewExpandingCells/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/animation/ListViewExpandingCells/res/drawable-xhdpi/ic_launcher.png b/samples/devbytes/animation/ListViewExpandingCells/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..71c6d76
--- /dev/null
+++ b/samples/devbytes/animation/ListViewExpandingCells/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/animation/ListViewExpandingCells/res/layout/activity_main.xml b/samples/devbytes/animation/ListViewExpandingCells/res/layout/activity_main.xml
new file mode 100644
index 0000000..d904a58
--- /dev/null
+++ b/samples/devbytes/animation/ListViewExpandingCells/res/layout/activity_main.xml
@@ -0,0 +1,21 @@
+<!-- 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.
+-->
+<com.example.android.expandingcells.ExpandingListView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/main_list_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context=".MainActivity" />
diff --git a/samples/devbytes/animation/ListViewExpandingCells/res/layout/list_view_item.xml b/samples/devbytes/animation/ListViewExpandingCells/res/layout/list_view_item.xml
new file mode 100644
index 0000000..c2c2209
--- /dev/null
+++ b/samples/devbytes/animation/ListViewExpandingCells/res/layout/list_view_item.xml
@@ -0,0 +1,68 @@
+<!-- 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@drawable/border"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:id="@+id/item_linear_layout"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:id="@+id/image_view"
+ android:layout_height="match_parent"
+ android:layout_width="0dp"
+ android:gravity="center_vertical"
+ android:layout_weight="1"
+ android:scaleType="center"/>
+
+ <TextView
+ android:id="@+id/title_view"
+ android:layout_height="fill_parent"
+ android:layout_width="0dp"
+ android:gravity="center"
+ android:layout_weight="2"
+ android:textStyle="bold"
+ android:textSize="22sp"
+ android:textColor="#ffffff"/>
+
+ </LinearLayout>
+
+ <com.example.android.expandingcells.ExpandingLayout
+ android:id="@+id/expanding_layout"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:visibility="gone">
+
+ <TextView
+ android:id="@+id/text_view"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:textStyle="bold"
+ android:textSize="22sp"
+ android:textColor="#ffffff"
+ android:gravity="center_horizontal"
+ android:paddingLeft="20dp"
+ android:paddingRight="20dp"
+ android:paddingBottom="20dp"/>
+
+ </com.example.android.expandingcells.ExpandingLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/samples/devbytes/animation/ListViewExpandingCells/res/values/strings.xml b/samples/devbytes/animation/ListViewExpandingCells/res/values/strings.xml
new file mode 100644
index 0000000..1fcee40
--- /dev/null
+++ b/samples/devbytes/animation/ListViewExpandingCells/res/values/strings.xml
@@ -0,0 +1,23 @@
+<!-- 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.
+-->
+
+<resources>
+
+ <string name="app_name">ExpandingCells</string>
+ <string name="short_lorem_ipsum">Lorem ipsum dolor sit amet, consectetur adipiscing elit</string>
+ <string name="medium_lorem_ipsum">"Pellentesque dictum sit amet sapien in faucibus. Curabitur fermentum, nulla quis placerat imperdiet, est nisi placerat arcu, non ornare erat justo at enim. Nam vitae porttitor sem. Quisque non quam nisi. Proin quis urna id elit ultrices cursus non tempus dolor"</string>
+ <string name="long_lorem_ipsum">"Mauris dapibus convallis massa, vitae ultrices est ultricies ut. Nam porttitor et metus ac bibendum. Nam at justo vitae felis lacinia ultrices laoreet ut arcu. Nam ac purus et turpis convallis mollis. Integer lorem eros, hendrerit imperdiet interdum vitae, sagittis eget ipsum. Donec dignissim tortor at felis fringilla, sed dignissim diam vulputate. Nam sit amet facilisis massa. Suspendisse posuere quam quis augue dapibus venenatis."</string>
+
+</resources>
diff --git a/samples/devbytes/animation/ListViewExpandingCells/src/com/example/android/expandingcells/CustomArrayAdapter.java b/samples/devbytes/animation/ListViewExpandingCells/src/com/example/android/expandingcells/CustomArrayAdapter.java
new file mode 100644
index 0000000..68919a2
--- /dev/null
+++ b/samples/devbytes/animation/ListViewExpandingCells/src/com/example/android/expandingcells/CustomArrayAdapter.java
@@ -0,0 +1,133 @@
+/*
+ * 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.expandingcells;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PorterDuff.Mode;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AbsListView;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import java.util.List;
+
+/**
+ * This is a custom array adapter used to populate the listview whose items will
+ * expand to display extra content in addition to the default display.
+ */
+public class CustomArrayAdapter extends ArrayAdapter<ExpandableListItem> {
+
+ private List<ExpandableListItem> mData;
+ private int mLayoutViewResourceId;
+
+ public CustomArrayAdapter(Context context, int layoutViewResourceId,
+ List<ExpandableListItem> data) {
+ super(context, layoutViewResourceId, data);
+ mData = data;
+ mLayoutViewResourceId = layoutViewResourceId;
+ }
+
+ /**
+ * Populates the item in the listview cell with the appropriate data. This method
+ * sets the thumbnail image, the title and the extra text. This method also updates
+ * the layout parameters of the item's view so that the image and title are centered
+ * in the bounds of the collapsed view, and such that the extra text is not displayed
+ * in the collapsed state of the cell.
+ */
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+
+ final ExpandableListItem object = mData.get(position);
+
+ if(convertView == null) {
+ LayoutInflater inflater = ((Activity) getContext()).getLayoutInflater();
+ convertView = inflater.inflate(mLayoutViewResourceId, parent, false);
+ }
+
+ LinearLayout linearLayout = (LinearLayout)(convertView.findViewById(
+ R.id.item_linear_layout));
+ LinearLayout.LayoutParams linearLayoutParams = new LinearLayout.LayoutParams
+ (AbsListView.LayoutParams.MATCH_PARENT, object.getCollapsedHeight());
+ linearLayout.setLayoutParams(linearLayoutParams);
+
+ ImageView imgView = (ImageView)convertView.findViewById(R.id.image_view);
+ TextView titleView = (TextView)convertView.findViewById(R.id.title_view);
+ TextView textView = (TextView)convertView.findViewById(R.id.text_view);
+
+ titleView.setText(object.getTitle());
+ imgView.setImageBitmap(getCroppedBitmap(BitmapFactory.decodeResource(getContext()
+ .getResources(), object.getImgResource(), null)));
+ textView.setText(object.getText());
+
+ convertView.setLayoutParams(new ListView.LayoutParams(AbsListView.LayoutParams.MATCH_PARENT,
+ AbsListView.LayoutParams.WRAP_CONTENT));
+
+ ExpandingLayout expandingLayout = (ExpandingLayout)convertView.findViewById(R.id
+ .expanding_layout);
+ expandingLayout.setExpandedHeight(object.getExpandedHeight());
+ expandingLayout.setSizeChangedListener(object);
+
+ if (!object.isExpanded()) {
+ expandingLayout.setVisibility(View.GONE);
+ } else {
+ expandingLayout.setVisibility(View.VISIBLE);
+ }
+
+ return convertView;
+ }
+
+ /**
+ * Crops a circle out of the thumbnail photo.
+ */
+ public Bitmap getCroppedBitmap(Bitmap bitmap) {
+ Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(),
+ Config.ARGB_8888);
+
+ final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
+
+ Canvas canvas = new Canvas(output);
+
+ final Paint paint = new Paint();
+ paint.setAntiAlias(true);
+
+ int halfWidth = bitmap.getWidth()/2;
+ int halfHeight = bitmap.getHeight()/2;
+
+ canvas.drawCircle(halfWidth, halfHeight, Math.max(halfWidth, halfHeight), paint);
+
+ paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
+
+ canvas.drawBitmap(bitmap, rect, rect, paint);
+
+ return output;
+ }
+
+
+}
\ No newline at end of file
diff --git a/samples/devbytes/animation/ListViewExpandingCells/src/com/example/android/expandingcells/ExpandableListItem.java b/samples/devbytes/animation/ListViewExpandingCells/src/com/example/android/expandingcells/ExpandableListItem.java
new file mode 100644
index 0000000..1eb4fc0
--- /dev/null
+++ b/samples/devbytes/animation/ListViewExpandingCells/src/com/example/android/expandingcells/ExpandableListItem.java
@@ -0,0 +1,87 @@
+/*
+ * 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.expandingcells;
+
+/**
+ * This custom object is used to populate the list adapter. It contains a reference
+ * to an image, title, and the extra text to be displayed. Furthermore, it keeps track
+ * of the current state (collapsed/expanded) of the corresponding item in the list,
+ * as well as store the height of the cell in its collapsed state.
+ */
+public class ExpandableListItem implements OnSizeChangedListener {
+
+ private String mTitle;
+ private String mText;
+ private boolean mIsExpanded;
+ private int mImgResource;
+ private int mCollapsedHeight;
+ private int mExpandedHeight;
+
+ public ExpandableListItem(String title, int imgResource, int collapsedHeight, String text) {
+ mTitle = title;
+ mImgResource = imgResource;
+ mCollapsedHeight = collapsedHeight;
+ mIsExpanded = false;
+ mText = text;
+ mExpandedHeight = -1;
+ }
+
+ public boolean isExpanded() {
+ return mIsExpanded;
+ }
+
+ public void setExpanded(boolean isExpanded) {
+ mIsExpanded = isExpanded;
+ }
+
+ public String getTitle() {
+ return mTitle;
+ }
+
+ public int getImgResource() {
+ return mImgResource;
+ }
+
+ public int getCollapsedHeight() {
+ return mCollapsedHeight;
+ }
+
+ public void setCollapsedHeight(int collapsedHeight) {
+ mCollapsedHeight = collapsedHeight;
+ }
+
+ public String getText() {
+ return mText;
+ }
+
+ public void setText(String text) {
+ mText = text;
+ }
+
+ public int getExpandedHeight() {
+ return mExpandedHeight;
+ }
+
+ public void setExpandedHeight(int expandedHeight) {
+ mExpandedHeight = expandedHeight;
+ }
+
+ @Override
+ public void onSizeChanged(int newHeight) {
+ setExpandedHeight(newHeight);
+ }
+}
diff --git a/samples/devbytes/animation/ListViewExpandingCells/src/com/example/android/expandingcells/ExpandingCells.java b/samples/devbytes/animation/ListViewExpandingCells/src/com/example/android/expandingcells/ExpandingCells.java
new file mode 100644
index 0000000..6d6d4f1
--- /dev/null
+++ b/samples/devbytes/animation/ListViewExpandingCells/src/com/example/android/expandingcells/ExpandingCells.java
@@ -0,0 +1,71 @@
+/*
+ * 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.expandingcells;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This activity creates a listview whose items can be clicked to expand and show
+ * additional content.
+ *
+ * In this specific demo, each item in a listview displays an image and a corresponding
+ * title. These two items are centered in the default (collapsed) state of the listview's
+ * item. When the item is clicked, it expands to display text of some varying length.
+ * The item persists in this expanded state (even if the user scrolls away and then scrolls
+ * back to the same location) until it is clicked again, at which point the cell collapses
+ * back to its default state.
+ */
+public class ExpandingCells extends Activity {
+
+ private final int CELL_DEFAULT_HEIGHT = 200;
+ private final int NUM_OF_CELLS = 30;
+
+ private ExpandingListView mListView;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ ExpandableListItem[] values = new ExpandableListItem[] {
+ new ExpandableListItem("Chameleon", R.drawable.chameleon, CELL_DEFAULT_HEIGHT,
+ getResources().getString(R.string.short_lorem_ipsum)),
+ new ExpandableListItem("Rock", R.drawable.rock, CELL_DEFAULT_HEIGHT,
+ getResources().getString(R.string.medium_lorem_ipsum)),
+ new ExpandableListItem("Flower", R.drawable.flower, CELL_DEFAULT_HEIGHT,
+ getResources().getString(R.string.long_lorem_ipsum)),
+ };
+
+ List<ExpandableListItem> mData = new ArrayList<ExpandableListItem>();
+
+ for (int i = 0; i < NUM_OF_CELLS; i++) {
+ ExpandableListItem obj = values[i % values.length];
+ mData.add(new ExpandableListItem(obj.getTitle(), obj.getImgResource(),
+ obj.getCollapsedHeight(), obj.getText()));
+ }
+
+ CustomArrayAdapter adapter = new CustomArrayAdapter(this, R.layout.list_view_item, mData);
+
+ mListView = (ExpandingListView)findViewById(R.id.main_list_view);
+ mListView.setAdapter(adapter);
+ mListView.setDivider(null);
+ }
+}
\ No newline at end of file
diff --git a/samples/devbytes/animation/ListViewExpandingCells/src/com/example/android/expandingcells/ExpandingLayout.java b/samples/devbytes/animation/ListViewExpandingCells/src/com/example/android/expandingcells/ExpandingLayout.java
new file mode 100644
index 0000000..5add667
--- /dev/null
+++ b/samples/devbytes/animation/ListViewExpandingCells/src/com/example/android/expandingcells/ExpandingLayout.java
@@ -0,0 +1,79 @@
+/*
+ * 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.expandingcells;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.RelativeLayout;
+
+/**
+ * This layout is used to contain the extra information that will be displayed
+ * when a certain cell is expanded. The custom relative layout is created in
+ * order to achieve a fading affect of this layout's contents as it is being
+ * expanded or collapsed as opposed to just fading the content in(out) after(before)
+ * the cell expands(collapses).
+ *
+ * During expansion, layout takes place so the full contents of this layout can
+ * be displayed. When the size changes to display the full contents of the layout,
+ * its height is stored. When the view is collapsing, this layout's height becomes 0
+ * since it is no longer in the visible part of the cell.By overriding onMeasure, and
+ * setting the height back to its max height, it is still visible during the collapse
+ * animation, and so, a fade out effect can be achieved.
+ */
+public class ExpandingLayout extends RelativeLayout {
+
+
+ private OnSizeChangedListener mSizeChangedListener;
+ private int mExpandedHeight = -1;
+
+ public ExpandingLayout(Context context) {
+ super(context);
+ }
+
+ public ExpandingLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public ExpandingLayout(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec) {
+ if (mExpandedHeight > 0) {
+ heightMeasureSpec = MeasureSpec.makeMeasureSpec(mExpandedHeight, MeasureSpec.AT_MOST);
+ }
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ protected void onSizeChanged (int w, int h, int oldw, int oldh) {
+ mExpandedHeight = h;
+ //Notifies the list data object corresponding to this layout that its size has changed.
+ mSizeChangedListener.onSizeChanged(h);
+ }
+
+ public int getExpandedHeight() {
+ return mExpandedHeight;
+ }
+
+ public void setExpandedHeight(int expandedHeight) {
+ mExpandedHeight = expandedHeight;
+ }
+
+ public void setSizeChangedListener(OnSizeChangedListener listener) {
+ mSizeChangedListener = listener;
+ }
+}
diff --git a/samples/devbytes/animation/ListViewExpandingCells/src/com/example/android/expandingcells/ExpandingListView.java b/samples/devbytes/animation/ListViewExpandingCells/src/com/example/android/expandingcells/ExpandingListView.java
new file mode 100644
index 0000000..0ba5d5f
--- /dev/null
+++ b/samples/devbytes/animation/ListViewExpandingCells/src/com/example/android/expandingcells/ExpandingListView.java
@@ -0,0 +1,540 @@
+/*
+ * 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.expandingcells;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.widget.AbsListView;
+import android.widget.AdapterView;
+import android.widget.ListView;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * A custom listview which supports the preview of extra content corresponding to each cell
+ * by clicking on the cell to hide and show the extra content.
+ */
+public class ExpandingListView extends ListView {
+
+ private boolean mShouldRemoveObserver = false;
+
+ private List<View> mViewsToDraw = new ArrayList<View>();
+
+ private int[] mTranslate;
+
+ public ExpandingListView(Context context) {
+ super(context);
+ init();
+ }
+
+ public ExpandingListView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init();
+ }
+
+ public ExpandingListView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ init();
+ }
+
+ private void init() {
+ setOnItemClickListener(mItemClickListener);
+ }
+
+ /**
+ * Listens for item clicks and expands or collapses the selected view depending on
+ * its current state.
+ */
+ private AdapterView.OnItemClickListener mItemClickListener = new AdapterView
+ .OnItemClickListener() {
+ @Override
+ public void onItemClick (AdapterView<?> parent, View view, int position, long id) {
+ ExpandableListItem viewObject = (ExpandableListItem)getItemAtPosition(getPositionForView
+ (view));
+ if (!viewObject.isExpanded()) {
+ expandView(view);
+ } else {
+ collapseView(view);
+ }
+ }
+ };
+
+ /**
+ * Calculates the top and bottom bound changes of the selected item. These values are
+ * also used to move the bounds of the items around the one that is actually being
+ * expanded or collapsed.
+ *
+ * This method can be modified to achieve different user experiences depending
+ * on how you want the cells to expand or collapse. In this specific demo, the cells
+ * always try to expand downwards (leaving top bound untouched), and similarly,
+ * collapse upwards (leaving top bound untouched). If the change in bounds
+ * results in the complete disappearance of a cell, its lower bound is moved is
+ * moved to the top of the screen so as not to hide any additional content that
+ * the user has not interacted with yet. Furthermore, if the collapsed cell is
+ * partially off screen when it is first clicked, it is translated such that its
+ * full contents are visible. Lastly, this behaviour varies slightly near the bottom
+ * of the listview in order to account for the fact that the bottom bounds of the actual
+ * listview cannot be modified.
+ */
+ private int[] getTopAndBottomTranslations(int top, int bottom, int yDelta,
+ boolean isExpanding) {
+ int yTranslateTop = 0;
+ int yTranslateBottom = yDelta;
+
+ int height = bottom - top;
+
+ if (isExpanding) {
+ boolean isOverTop = top < 0;
+ boolean isBelowBottom = (top + height + yDelta) > getHeight();
+ if (isOverTop) {
+ yTranslateTop = top;
+ yTranslateBottom = yDelta - yTranslateTop;
+ } else if (isBelowBottom){
+ int deltaBelow = top + height + yDelta - getHeight();
+ yTranslateTop = top - deltaBelow < 0 ? top : deltaBelow;
+ yTranslateBottom = yDelta - yTranslateTop;
+ }
+ } else {
+ int offset = computeVerticalScrollOffset();
+ int range = computeVerticalScrollRange();
+ int extent = computeVerticalScrollExtent();
+ int leftoverExtent = range-offset - extent;
+
+ boolean isCollapsingBelowBottom = (yTranslateBottom > leftoverExtent);
+ boolean isCellCompletelyDisappearing = bottom - yTranslateBottom < 0;
+
+ if (isCollapsingBelowBottom) {
+ yTranslateTop = yTranslateBottom - leftoverExtent;
+ yTranslateBottom = yDelta - yTranslateTop;
+ } else if (isCellCompletelyDisappearing) {
+ yTranslateBottom = bottom;
+ yTranslateTop = yDelta - yTranslateBottom;
+ }
+ }
+
+ return new int[] {yTranslateTop, yTranslateBottom};
+ }
+
+ /**
+ * This method expands the view that was clicked and animates all the views
+ * around it to make room for the expanding view. There are several steps required
+ * to do this which are outlined below.
+ *
+ * 1. Store the current top and bottom bounds of each visible item in the listview.
+ * 2. Update the layout parameters of the selected view. In the context of this
+ * method, the view should be originally collapsed and set to some custom height.
+ * The layout parameters are updated so as to wrap the content of the additional
+ * text that is to be displayed.
+ *
+ * After invoking a layout to take place, the listview will order all the items
+ * such that there is space for each view. This layout will be independent of what
+ * the bounds of the items were prior to the layout so two pre-draw passes will
+ * be made. This is necessary because after the layout takes place, some views that
+ * were visible before the layout may now be off bounds but a reference to these
+ * views is required so the animation completes as intended.
+ *
+ * 3. The first predraw pass will set the bounds of all the visible items to
+ * their original location before the layout took place and then force another
+ * layout. Since the bounds of the cells cannot be set directly, the method
+ * setSelectionFromTop can be used to achieve a very similar effect.
+ * 4. The expanding view's bounds are animated to what the final values should be
+ * from the original bounds.
+ * 5. The bounds above the expanding view are animated upwards while the bounds
+ * below the expanding view are animated downwards.
+ * 6. The extra text is faded in as its contents become visible throughout the
+ * animation process.
+ *
+ * It is important to note that the listview is disabled during the animation
+ * because the scrolling behaviour is unpredictable if the bounds of the items
+ * within the listview are not constant during the scroll.
+ */
+
+ private void expandView(final View view) {
+ final ExpandableListItem viewObject = (ExpandableListItem)getItemAtPosition(getPositionForView
+ (view));
+
+ /* Store the original top and bottom bounds of all the cells.*/
+ final int oldTop = view.getTop();
+ final int oldBottom = view.getBottom();
+
+ final HashMap<View, int[]> oldCoordinates = new HashMap<View, int[]>();
+
+ int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ View v = getChildAt(i);
+ v.setHasTransientState(true);
+ oldCoordinates.put(v, new int[] {v.getTop(), v.getBottom()});
+ }
+
+ /* Update the layout so the extra content becomes visible.*/
+ final View expandingLayout = view.findViewById(R.id.expanding_layout);
+ expandingLayout.setVisibility(View.VISIBLE);
+
+ /* Add an onPreDraw Listener to the listview. onPreDraw will get invoked after onLayout
+ * and onMeasure have run but before anything has been drawn. This
+ * means that the final post layout properties for all the items have already been
+ * determined, but still have not been rendered onto the screen.*/
+ final ViewTreeObserver observer = getViewTreeObserver();
+ observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
+
+ @Override
+ public boolean onPreDraw() {
+ /* Determine if this is the first or second pass.*/
+ if (!mShouldRemoveObserver) {
+ mShouldRemoveObserver = true;
+
+ /* Calculate what the parameters should be for setSelectionFromTop.
+ * The ListView must be offset in a way, such that after the animation
+ * takes place, all the cells that remain visible are rendered completely
+ * by the ListView.*/
+ int newTop = view.getTop();
+ int newBottom = view.getBottom();
+
+ int newHeight = newBottom - newTop;
+ int oldHeight = oldBottom - oldTop;
+ int delta = newHeight - oldHeight;
+
+ mTranslate = getTopAndBottomTranslations(oldTop, oldBottom, delta, true);
+
+ int currentTop = view.getTop();
+ int futureTop = oldTop - mTranslate[0];
+
+ int firstChildStartTop = getChildAt(0).getTop();
+ int firstVisiblePosition = getFirstVisiblePosition();
+ int deltaTop = currentTop - futureTop;
+
+ int i;
+ int childCount = getChildCount();
+ for (i = 0; i < childCount; i++) {
+ View v = getChildAt(i);
+ int height = v.getBottom() - Math.max(0, v.getTop());
+ if (deltaTop - height > 0) {
+ firstVisiblePosition++;
+ deltaTop -= height;
+ } else {
+ break;
+ }
+ }
+
+ if (i > 0) {
+ firstChildStartTop = 0;
+ }
+
+ setSelectionFromTop(firstVisiblePosition, firstChildStartTop - deltaTop);
+
+ /* Request another layout to update the layout parameters of the cells.*/
+ requestLayout();
+
+ /* Return false such that the ListView does not redraw its contents on
+ * this layout but only updates all the parameters associated with its
+ * children.*/
+ return false;
+ }
+
+ /* Remove the predraw listener so this method does not keep getting called. */
+ mShouldRemoveObserver = false;
+ observer.removeOnPreDrawListener(this);
+
+ int yTranslateTop = mTranslate[0];
+ int yTranslateBottom = mTranslate[1];
+
+ ArrayList <Animator> animations = new ArrayList<Animator>();
+
+ int index = indexOfChild(view);
+
+ /* Loop through all the views that were on the screen before the cell was
+ * expanded. Some cells will still be children of the ListView while
+ * others will not. The cells that remain children of the ListView
+ * simply have their bounds animated appropriately. The cells that are no
+ * longer children of the ListView also have their bounds animated, but
+ * must also be added to a list of views which will be drawn in dispatchDraw.*/
+ for (View v: oldCoordinates.keySet()) {
+ int[] old = oldCoordinates.get(v);
+ v.setTop(old[0]);
+ v.setBottom(old[1]);
+ if (v.getParent() == null) {
+ mViewsToDraw.add(v);
+ int delta = old[0] < oldTop ? -yTranslateTop : yTranslateBottom;
+ animations.add(getAnimation(v, delta, delta));
+ } else {
+ int i = indexOfChild(v);
+ if (v != view) {
+ int delta = i > index ? yTranslateBottom : -yTranslateTop;
+ animations.add(getAnimation(v, delta, delta));
+ }
+ v.setHasTransientState(false);
+ }
+ }
+
+ /* Adds animation for expanding the cell that was clicked. */
+ animations.add(getAnimation(view, -yTranslateTop, yTranslateBottom));
+
+ /* Adds an animation for fading in the extra content. */
+ animations.add(ObjectAnimator.ofFloat(view.findViewById(R.id.expanding_layout),
+ View.ALPHA, 0, 1));
+
+ /* Disabled the ListView for the duration of the animation.*/
+ setEnabled(false);
+ setClickable(false);
+
+ /* Play all the animations created above together at the same time. */
+ AnimatorSet s = new AnimatorSet();
+ s.playTogether(animations);
+ s.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ viewObject.setExpanded(true);
+ setEnabled(true);
+ setClickable(true);
+ if (mViewsToDraw.size() > 0) {
+ for (View v : mViewsToDraw) {
+ v.setHasTransientState(false);
+ }
+ }
+ mViewsToDraw.clear();
+ }
+ });
+ s.start();
+ return true;
+ }
+ });
+ }
+
+ /**
+ * By overriding dispatchDraw, we can draw the cells that disappear during the
+ * expansion process. When the cell expands, some items below or above the expanding
+ * cell may be moved off screen and are thus no longer children of the ListView's
+ * layout. By storing a reference to these views prior to the layout, and
+ * guaranteeing that these cells do not get recycled, the cells can be drawn
+ * directly onto the canvas during the animation process. After the animation
+ * completes, the references to the extra views can then be discarded.
+ */
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ super.dispatchDraw(canvas);
+
+ if (mViewsToDraw.size() == 0) {
+ return;
+ }
+
+ for (View v: mViewsToDraw) {
+ canvas.translate(0, v.getTop());
+ v.draw(canvas);
+ canvas.translate(0, -v.getTop());
+ }
+ }
+
+ /**
+ * This method collapses the view that was clicked and animates all the views
+ * around it to close around the collapsing view. There are several steps required
+ * to do this which are outlined below.
+ *
+ * 1. Update the layout parameters of the view clicked so as to minimize its height
+ * to the original collapsed (default) state.
+ * 2. After invoking a layout, the listview will shift all the cells so as to display
+ * them most efficiently. Therefore, during the first predraw pass, the listview
+ * must be offset by some amount such that given the custom bound change upon
+ * collapse, all the cells that need to be on the screen after the layout
+ * are rendered by the listview.
+ * 3. On the second predraw pass, all the items are first returned to their original
+ * location (before the first layout).
+ * 4. The collapsing view's bounds are animated to what the final values should be.
+ * 5. The bounds above the collapsing view are animated downwards while the bounds
+ * below the collapsing view are animated upwards.
+ * 6. The extra text is faded out as its contents become visible throughout the
+ * animation process.
+ */
+
+ private void collapseView(final View view) {
+ final ExpandableListItem viewObject = (ExpandableListItem)getItemAtPosition
+ (getPositionForView(view));
+
+ /* Store the original top and bottom bounds of all the cells.*/
+ final int oldTop = view.getTop();
+ final int oldBottom = view.getBottom();
+
+ final HashMap<View, int[]> oldCoordinates = new HashMap<View, int[]>();
+
+ int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ View v = getChildAt(i);
+ v.setHasTransientState(true);
+ oldCoordinates.put(v, new int [] {v.getTop(), v.getBottom()});
+ }
+
+ /* Update the layout so the extra content becomes invisible.*/
+ view.setLayoutParams(new AbsListView.LayoutParams(AbsListView.LayoutParams.MATCH_PARENT,
+ viewObject.getCollapsedHeight()));
+
+ /* Add an onPreDraw listener. */
+ final ViewTreeObserver observer = getViewTreeObserver();
+ observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
+
+ @Override
+ public boolean onPreDraw() {
+
+ if (!mShouldRemoveObserver) {
+ /*Same as for expandingView, the parameters for setSelectionFromTop must
+ * be determined such that the necessary cells of the ListView are rendered
+ * and added to it.*/
+ mShouldRemoveObserver = true;
+
+ int newTop = view.getTop();
+ int newBottom = view.getBottom();
+
+ int newHeight = newBottom - newTop;
+ int oldHeight = oldBottom - oldTop;
+ int deltaHeight = oldHeight - newHeight;
+
+ mTranslate = getTopAndBottomTranslations(oldTop, oldBottom, deltaHeight, false);
+
+ int currentTop = view.getTop();
+ int futureTop = oldTop + mTranslate[0];
+
+ int firstChildStartTop = getChildAt(0).getTop();
+ int firstVisiblePosition = getFirstVisiblePosition();
+ int deltaTop = currentTop - futureTop;
+
+ int i;
+ int childCount = getChildCount();
+ for (i = 0; i < childCount; i++) {
+ View v = getChildAt(i);
+ int height = v.getBottom() - Math.max(0, v.getTop());
+ if (deltaTop - height > 0) {
+ firstVisiblePosition++;
+ deltaTop -= height;
+ } else {
+ break;
+ }
+ }
+
+ if (i > 0) {
+ firstChildStartTop = 0;
+ }
+
+ setSelectionFromTop(firstVisiblePosition, firstChildStartTop - deltaTop);
+
+ requestLayout();
+
+ return false;
+ }
+
+ mShouldRemoveObserver = false;
+ observer.removeOnPreDrawListener(this);
+
+ int yTranslateTop = mTranslate[0];
+ int yTranslateBottom = mTranslate[1];
+
+ int index = indexOfChild(view);
+ int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ View v = getChildAt(i);
+ int [] old = oldCoordinates.get(v);
+ if (old != null) {
+ /* If the cell was present in the ListView before the collapse and
+ * after the collapse then the bounds are reset to their old values.*/
+ v.setTop(old[0]);
+ v.setBottom(old[1]);
+ v.setHasTransientState(false);
+ } else {
+ /* If the cell is present in the ListView after the collapse but
+ * not before the collapse then the bounds are calculated using
+ * the bottom and top translation of the collapsing cell.*/
+ int delta = i > index ? yTranslateBottom : -yTranslateTop;
+ v.setTop(v.getTop() + delta);
+ v.setBottom(v.getBottom() + delta);
+ }
+ }
+
+ final View expandingLayout = view.findViewById (R.id.expanding_layout);
+
+ /* Animates all the cells present on the screen after the collapse. */
+ ArrayList <Animator> animations = new ArrayList<Animator>();
+ for (int i = 0; i < childCount; i++) {
+ View v = getChildAt(i);
+ if (v != view) {
+ float diff = i > index ? -yTranslateBottom : yTranslateTop;
+ animations.add(getAnimation(v, diff, diff));
+ }
+ }
+
+
+ /* Adds animation for collapsing the cell that was clicked. */
+ animations.add(getAnimation(view, yTranslateTop, -yTranslateBottom));
+
+ /* Adds an animation for fading out the extra content. */
+ animations.add(ObjectAnimator.ofFloat(expandingLayout, View.ALPHA, 1, 0));
+
+ /* Disabled the ListView for the duration of the animation.*/
+ setEnabled(false);
+ setClickable(false);
+
+ /* Play all the animations created above together at the same time. */
+ AnimatorSet s = new AnimatorSet();
+ s.playTogether(animations);
+ s.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ expandingLayout.setVisibility(View.GONE);
+ view.setLayoutParams(new AbsListView.LayoutParams(AbsListView
+ .LayoutParams.MATCH_PARENT, AbsListView.LayoutParams.WRAP_CONTENT));
+ viewObject.setExpanded(false);
+ setEnabled(true);
+ setClickable(true);
+ /* Note that alpha must be set back to 1 in case this view is reused
+ * by a cell that was expanded, but not yet collapsed, so its state
+ * should persist in an expanded state with the extra content visible.*/
+ expandingLayout.setAlpha(1);
+ }
+ });
+ s.start();
+
+ return true;
+ }
+ });
+ }
+
+ /**
+ * This method takes some view and the values by which its top and bottom bounds
+ * should be changed by. Given these params, an animation which will animate
+ * these bound changes is created and returned.
+ */
+ private Animator getAnimation(final View view, float translateTop, float translateBottom) {
+
+ int top = view.getTop();
+ int bottom = view.getBottom();
+
+ int endTop = (int)(top + translateTop);
+ int endBottom = (int)(bottom + translateBottom);
+
+ PropertyValuesHolder translationTop = PropertyValuesHolder.ofInt("top", top, endTop);
+ PropertyValuesHolder translationBottom = PropertyValuesHolder.ofInt("bottom", bottom,
+ endBottom);
+
+ return ObjectAnimator.ofPropertyValuesHolder(view, translationTop, translationBottom);
+ }
+}
diff --git a/samples/devbytes/animation/ListViewExpandingCells/src/com/example/android/expandingcells/OnSizeChangedListener.java b/samples/devbytes/animation/ListViewExpandingCells/src/com/example/android/expandingcells/OnSizeChangedListener.java
new file mode 100644
index 0000000..ec51950
--- /dev/null
+++ b/samples/devbytes/animation/ListViewExpandingCells/src/com/example/android/expandingcells/OnSizeChangedListener.java
@@ -0,0 +1,25 @@
+/*
+ * 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.expandingcells;
+
+/**
+ * A listener used to update the list data object when the corresponding expanding
+ * layout experiences a size change.
+ */
+public interface OnSizeChangedListener {
+ public void onSizeChanged(int newHeight);
+}
diff --git a/samples/devbytes/graphics/FoldingLayout/AndroidManifest.xml b/samples/devbytes/graphics/FoldingLayout/AndroidManifest.xml
new file mode 100644
index 0000000..13758d7
--- /dev/null
+++ b/samples/devbytes/graphics/FoldingLayout/AndroidManifest.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.android.foldinglayout"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <uses-permission android:name="android.permission.CAMERA" />
+ <uses-feature android:name="android.hardware.camera" />
+ <uses-sdk android:minSdkVersion="17"
+ android:targetSdkVersion="18"/>
+ <application android:label="@string/app_name" android:icon="@drawable/ic_launcher">
+ <activity android:name=".FoldingLayoutActivity"
+ android:label="@string/app_name">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/samples/devbytes/graphics/FoldingLayout/res/drawable-hdpi/ic_launcher.png b/samples/devbytes/graphics/FoldingLayout/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..96a442e
--- /dev/null
+++ b/samples/devbytes/graphics/FoldingLayout/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/graphics/FoldingLayout/res/drawable-hdpi/image.jpg b/samples/devbytes/graphics/FoldingLayout/res/drawable-hdpi/image.jpg
new file mode 100644
index 0000000..60ce9f2
--- /dev/null
+++ b/samples/devbytes/graphics/FoldingLayout/res/drawable-hdpi/image.jpg
Binary files differ
diff --git a/samples/devbytes/graphics/FoldingLayout/res/drawable-ldpi/ic_launcher.png b/samples/devbytes/graphics/FoldingLayout/res/drawable-ldpi/ic_launcher.png
new file mode 100644
index 0000000..9923872
--- /dev/null
+++ b/samples/devbytes/graphics/FoldingLayout/res/drawable-ldpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/graphics/FoldingLayout/res/drawable-mdpi/ic_launcher.png b/samples/devbytes/graphics/FoldingLayout/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..359047d
--- /dev/null
+++ b/samples/devbytes/graphics/FoldingLayout/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/graphics/FoldingLayout/res/drawable-xhdpi/ic_launcher.png b/samples/devbytes/graphics/FoldingLayout/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..71c6d76
--- /dev/null
+++ b/samples/devbytes/graphics/FoldingLayout/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/graphics/FoldingLayout/res/layout/activity_fold.xml b/samples/devbytes/graphics/FoldingLayout/res/layout/activity_fold.xml
new file mode 100644
index 0000000..9ed3bc5
--- /dev/null
+++ b/samples/devbytes/graphics/FoldingLayout/res/layout/activity_fold.xml
@@ -0,0 +1,41 @@
+<!-- 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <com.example.android.foldinglayout.FoldingLayout
+ android:layout_weight="1"
+ android:id="@+id/fold_view"
+ android:layout_width="match_parent"
+ android:layout_height="0dp">
+
+ <ImageView
+ android:id="@+id/image_view"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:scaleType="fitXY"/>
+
+ </com.example.android.foldinglayout.FoldingLayout>
+
+ <SeekBar
+ android:id="@+id/anchor_seek_bar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:max="100"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/samples/devbytes/graphics/FoldingLayout/res/layout/spinner.xml b/samples/devbytes/graphics/FoldingLayout/res/layout/spinner.xml
new file mode 100644
index 0000000..c35133d
--- /dev/null
+++ b/samples/devbytes/graphics/FoldingLayout/res/layout/spinner.xml
@@ -0,0 +1,18 @@
+<!-- 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.
+-->
+<Spinner xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:entries="@array/num_of_folds_array" />
\ No newline at end of file
diff --git a/samples/devbytes/graphics/FoldingLayout/res/menu/fold.xml b/samples/devbytes/graphics/FoldingLayout/res/menu/fold.xml
new file mode 100644
index 0000000..a0231bd
--- /dev/null
+++ b/samples/devbytes/graphics/FoldingLayout/res/menu/fold.xml
@@ -0,0 +1,41 @@
+<!-- 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.
+-->
+<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+
+ <item
+ android:id="@+id/toggle_orientation"
+ android:showAsAction="never"
+ android:title="@string/vertical"/>
+
+ <item
+ android:id="@+id/num_of_folds"
+ android:showAsAction="ifRoom"
+ android:actionLayout="@layout/spinner"/>
+
+ <item
+ android:title="@string/sepia_effect_off"
+ android:id="@+id/sepia"
+ android:checkable="true"/>
+
+ <item
+ android:title="@string/camera_feed"
+ android:id="@+id/camera_feed"/>
+
+ <item
+ android:id="@+id/animate_fold"
+ android:showAsAction="never"
+ android:title="@string/animate"/>
+
+</menu>
\ No newline at end of file
diff --git a/samples/devbytes/graphics/FoldingLayout/res/menu/fold_with_bug.xml b/samples/devbytes/graphics/FoldingLayout/res/menu/fold_with_bug.xml
new file mode 100644
index 0000000..44631ee
--- /dev/null
+++ b/samples/devbytes/graphics/FoldingLayout/res/menu/fold_with_bug.xml
@@ -0,0 +1,32 @@
+<!-- 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.
+-->
+<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+
+ <item
+ android:id="@+id/toggle_orientation"
+ android:showAsAction="never"
+ android:title="@string/vertical"/>
+
+ <item
+ android:id="@+id/num_of_folds"
+ android:showAsAction="ifRoom"
+ android:actionLayout="@layout/spinner"/>
+
+ <item
+ android:id="@+id/animate_fold"
+ android:showAsAction="never"
+ android:title="@string/animate"/>
+
+</menu>
\ No newline at end of file
diff --git a/samples/devbytes/graphics/FoldingLayout/res/values/strings.xml b/samples/devbytes/graphics/FoldingLayout/res/values/strings.xml
new file mode 100644
index 0000000..181b15d
--- /dev/null
+++ b/samples/devbytes/graphics/FoldingLayout/res/values/strings.xml
@@ -0,0 +1,42 @@
+<!-- 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.
+-->
+<resources>
+
+ <string name="app_name">FoldingLayout</string>
+ <string name="title_activity_fold">FoldActivity</string>
+
+ <string name="horizontal">Horizontal</string>
+ <string name="vertical">Vertical</string>
+ <string name="num_of_folds">Number Of Folds</string>
+
+ <string name="animate">Animate</string>
+
+ <string name="camera_feed">Camera Feed</string>
+ <string name="static_image">Static Image</string>
+
+ <string name="sepia_effect_off">Sepia Off</string>
+
+ <string-array name="num_of_folds_array">
+ <item>2</item>
+ <item>3</item>
+ <item>4</item>
+ <item>5</item>
+ <item>6</item>
+ <item>7</item>
+ <item>8</item>
+ <item>1</item>
+ </string-array>
+
+</resources>
diff --git a/samples/devbytes/graphics/FoldingLayout/src/com/example/android/foldinglayout/FoldingLayout.java b/samples/devbytes/graphics/FoldingLayout/src/com/example/android/foldinglayout/FoldingLayout.java
new file mode 100644
index 0000000..8afb27e
--- /dev/null
+++ b/samples/devbytes/graphics/FoldingLayout/src/com/example/android/foldinglayout/FoldingLayout.java
@@ -0,0 +1,545 @@
+/*
+ * 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.foldinglayout;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.LinearGradient;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Paint.Style;
+import android.graphics.Rect;
+import android.graphics.Shader.TileMode;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * The folding layout where the number of folds, the anchor point and the
+ * orientation of the fold can be specified. Each of these parameters can
+ * be modified individually and updates and resets the fold to a default
+ * (unfolded) state. The fold factor varies between 0 (completely unfolded
+ * flat image) to 1.0 (completely folded, non-visible image).
+ *
+ * This layout throws an exception if there is more than one child added to the view.
+ * For more complicated view hierarchy's inside the folding layout, the views should all
+ * be nested inside 1 parent layout.
+ *
+ * This layout folds the contents of its child in real time. By applying matrix
+ * transformations when drawing to canvas, the contents of the child may change as
+ * the fold takes place. It is important to note that there are jagged edges about
+ * the perimeter of the layout as a result of applying transformations to a rectangle.
+ * This can be avoided by having the child of this layout wrap its content inside a
+ * 1 pixel transparent border. This will cause an anti-aliasing like effect and smoothen
+ * out the edges.
+ *
+ */
+public class FoldingLayout extends ViewGroup {
+
+ public static enum Orientation {
+ VERTICAL,
+ HORIZONTAL
+ }
+
+ private final String FOLDING_VIEW_EXCEPTION_MESSAGE = "Folding Layout can only 1 child at " +
+ "most";
+
+ private final float SHADING_ALPHA = 0.8f;
+ private final float SHADING_FACTOR = 0.5f;
+ private final int DEPTH_CONSTANT = 1500;
+ private final int NUM_OF_POLY_POINTS = 8;
+
+ private Rect[] mFoldRectArray;
+
+ private Matrix [] mMatrix;
+
+ private Orientation mOrientation = Orientation.HORIZONTAL;
+
+ private float mAnchorFactor = 0;
+ private float mFoldFactor = 0;
+
+ private int mNumberOfFolds = 2;
+
+ private boolean mIsHorizontal = true;
+
+ private int mOriginalWidth = 0;
+ private int mOriginalHeight = 0;
+
+ private float mFoldMaxWidth = 0;
+ private float mFoldMaxHeight = 0;
+ private float mFoldDrawWidth = 0;
+ private float mFoldDrawHeight = 0;
+
+ private boolean mIsFoldPrepared = false;
+ private boolean mShouldDraw = true;
+
+ private Paint mSolidShadow;
+ private Paint mGradientShadow;
+ private LinearGradient mShadowLinearGradient;
+ private Matrix mShadowGradientMatrix;
+
+ private float [] mSrc;
+ private float [] mDst;
+
+ private OnFoldListener mFoldListener;
+
+ private float mPreviousFoldFactor = 0;
+
+ private Bitmap mFullBitmap;
+ private Rect mDstRect;
+
+ public FoldingLayout(Context context) {
+ super(context);
+ }
+
+ public FoldingLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public FoldingLayout(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ @Override
+ protected boolean addViewInLayout(View child, int index, LayoutParams params,
+ boolean preventRequestLayout) {
+ throwCustomException(getChildCount());
+ boolean returnValue = super.addViewInLayout(child, index, params, preventRequestLayout);
+ return returnValue;
+ }
+
+ @Override
+ public void addView(View child, int index, LayoutParams params) {
+ throwCustomException(getChildCount());
+ super.addView(child, index, params);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ View child = getChildAt(0);
+ measureChild(child,widthMeasureSpec, heightMeasureSpec);
+ setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ View child = getChildAt(0);
+ child.layout(0, 0, child.getMeasuredWidth(), child.getMeasuredHeight());
+ updateFold();
+ }
+
+ /**
+ * The custom exception to be thrown so as to limit the number of views in this
+ * layout to at most one.
+ */
+ private class NumberOfFoldingLayoutChildrenException extends RuntimeException {
+ public NumberOfFoldingLayoutChildrenException(String message) {
+ super(message);
+ }
+ }
+
+ /** Throws an exception if the number of views added to this layout exceeds one.*/
+ private void throwCustomException (int numOfChildViews) {
+ if (numOfChildViews == 1) {
+ throw new NumberOfFoldingLayoutChildrenException(FOLDING_VIEW_EXCEPTION_MESSAGE);
+ }
+ }
+
+ public void setFoldListener(OnFoldListener foldListener) {
+ mFoldListener = foldListener;
+ }
+
+ /**
+ * Sets the fold factor of the folding view and updates all the corresponding
+ * matrices and values to account for the new fold factor. Once that is complete,
+ * it redraws itself with the new fold. */
+ public void setFoldFactor(float foldFactor) {
+ if (foldFactor != mFoldFactor) {
+ mFoldFactor = foldFactor;
+ calculateMatrices();
+ invalidate();
+ }
+ }
+
+ public void setOrientation(Orientation orientation) {
+ if (orientation != mOrientation) {
+ mOrientation = orientation;
+ updateFold();
+ }
+ }
+
+ public void setAnchorFactor(float anchorFactor) {
+ if (anchorFactor != mAnchorFactor) {
+ mAnchorFactor = anchorFactor;
+ updateFold();
+ }
+ }
+
+ public void setNumberOfFolds(int numberOfFolds) {
+ if (numberOfFolds != mNumberOfFolds) {
+ mNumberOfFolds = numberOfFolds;
+ updateFold();
+ }
+ }
+
+ public float getAnchorFactor() {
+ return mAnchorFactor;
+ }
+
+ public Orientation getOrientation() {
+ return mOrientation;
+ }
+
+ public float getFoldFactor() {
+ return mFoldFactor;
+ }
+
+ public int getNumberOfFolds() {
+ return mNumberOfFolds;
+ }
+
+ private void updateFold() {
+ prepareFold(mOrientation, mAnchorFactor, mNumberOfFolds);
+ calculateMatrices();
+ invalidate();
+ }
+
+ /**
+ * This method is called in order to update the fold's orientation, anchor
+ * point and number of folds. This creates the necessary setup in order to
+ * prepare the layout for a fold with the specified parameters. Some of the
+ * dimensions required for the folding transformation are also acquired here.
+ *
+ * After this method is called, it will be in a completely unfolded state by default.
+ */
+ private void prepareFold(Orientation orientation, float anchorFactor, int numberOfFolds) {
+
+ mSrc = new float[NUM_OF_POLY_POINTS];
+ mDst = new float[NUM_OF_POLY_POINTS];
+
+ mDstRect = new Rect();
+
+ mFoldFactor = 0;
+ mPreviousFoldFactor = 0;
+
+ mIsFoldPrepared = false;
+
+ mSolidShadow = new Paint();
+ mGradientShadow = new Paint();
+
+ mOrientation = orientation;
+ mIsHorizontal = (orientation == Orientation.HORIZONTAL);
+
+ if (mIsHorizontal) {
+ mShadowLinearGradient = new LinearGradient(0, 0, SHADING_FACTOR, 0, Color.BLACK,
+ Color.TRANSPARENT, TileMode.CLAMP);
+ } else {
+ mShadowLinearGradient = new LinearGradient(0, 0, 0, SHADING_FACTOR, Color.BLACK,
+ Color.TRANSPARENT, TileMode.CLAMP);
+ }
+
+ mGradientShadow.setStyle(Style.FILL);
+ mGradientShadow.setShader(mShadowLinearGradient);
+ mShadowGradientMatrix = new Matrix();
+
+ mAnchorFactor = anchorFactor;
+ mNumberOfFolds = numberOfFolds;
+
+ mOriginalWidth = getMeasuredWidth();
+ mOriginalHeight = getMeasuredHeight();
+
+ mFoldRectArray = new Rect[mNumberOfFolds];
+ mMatrix = new Matrix [mNumberOfFolds];
+
+ for (int x = 0; x < mNumberOfFolds; x++) {
+ mMatrix[x] = new Matrix();
+ }
+
+ int h = mOriginalHeight;
+ int w = mOriginalWidth;
+
+ if (FoldingLayoutActivity.IS_JBMR2) {
+ mFullBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(mFullBitmap);
+ getChildAt(0).draw(canvas);
+ }
+
+ int delta = Math.round(mIsHorizontal ? ((float) w) / ((float) mNumberOfFolds) :
+ ((float) h) /((float) mNumberOfFolds));
+
+ /* Loops through the number of folds and segments the full layout into a number
+ * of smaller equal components. If the number of folds is odd, then one of the
+ * components will be smaller than all the rest. Note that deltap below handles
+ * the calculation for an odd number of folds.*/
+ for (int x = 0; x < mNumberOfFolds; x++) {
+ if (mIsHorizontal) {
+ int deltap = (x + 1) * delta > w ? w - x * delta : delta;
+ mFoldRectArray[x] = new Rect(x * delta, 0, x * delta + deltap, h);
+ } else {
+ int deltap = (x + 1) * delta > h ? h - x * delta : delta;
+ mFoldRectArray[x] = new Rect(0, x * delta, w, x * delta + deltap);
+ }
+ }
+
+ if (mIsHorizontal) {
+ mFoldMaxHeight = h;
+ mFoldMaxWidth = delta;
+ } else {
+ mFoldMaxHeight = delta;
+ mFoldMaxWidth = w;
+ }
+
+ mIsFoldPrepared = true;
+ }
+
+ /*
+ * Calculates the transformation matrices used to draw each of the separate folding
+ * segments from this view.
+ */
+ private void calculateMatrices() {
+
+ mShouldDraw = true;
+
+ if (!mIsFoldPrepared) {
+ return;
+ }
+
+ /** If the fold factor is 1 than the folding view should not be seen
+ * and the canvas can be left completely empty. */
+ if (mFoldFactor == 1) {
+ mShouldDraw = false;
+ return;
+ }
+
+ if (mFoldFactor == 0 && mPreviousFoldFactor > 0) {
+ mFoldListener.onEndFold();
+ }
+
+ if (mPreviousFoldFactor == 0 && mFoldFactor > 0) {
+ mFoldListener.onStartFold();
+ }
+
+ mPreviousFoldFactor = mFoldFactor;
+
+ /* Reset all the transformation matrices back to identity before computing
+ * the new transformation */
+ for (int x = 0; x < mNumberOfFolds; x++) {
+ mMatrix[x].reset();
+ }
+
+ float cTranslationFactor = 1 - mFoldFactor;
+
+ float translatedDistance = mIsHorizontal ? mOriginalWidth * cTranslationFactor :
+ mOriginalHeight * cTranslationFactor;
+
+ float translatedDistancePerFold = Math.round(translatedDistance / mNumberOfFolds);
+
+ /* For an odd number of folds, the rounding error may cause the
+ * translatedDistancePerFold to be grater than the max fold width or height. */
+ mFoldDrawWidth = mFoldMaxWidth < translatedDistancePerFold ?
+ translatedDistancePerFold : mFoldMaxWidth;
+ mFoldDrawHeight = mFoldMaxHeight < translatedDistancePerFold ?
+ translatedDistancePerFold : mFoldMaxHeight;
+
+ float translatedDistanceFoldSquared = translatedDistancePerFold * translatedDistancePerFold;
+
+ /* Calculate the depth of the fold into the screen using pythagorean theorem. */
+ float depth = mIsHorizontal ?
+ (float)Math.sqrt((double)(mFoldDrawWidth * mFoldDrawWidth -
+ translatedDistanceFoldSquared)) :
+ (float)Math.sqrt((double)(mFoldDrawHeight * mFoldDrawHeight -
+ translatedDistanceFoldSquared));
+
+ /* The size of some object is always inversely proportional to the distance
+ * it is away from the viewpoint. The constant can be varied to to affect the
+ * amount of perspective. */
+ float scaleFactor = DEPTH_CONSTANT / (DEPTH_CONSTANT + depth);
+
+ float scaledWidth, scaledHeight, bottomScaledPoint, topScaledPoint, rightScaledPoint,
+ leftScaledPoint;
+
+ if (mIsHorizontal) {
+ scaledWidth = mFoldDrawWidth * cTranslationFactor;
+ scaledHeight = mFoldDrawHeight * scaleFactor;
+ } else {
+ scaledWidth = mFoldDrawWidth * scaleFactor;
+ scaledHeight = mFoldDrawHeight * cTranslationFactor;
+ }
+
+ topScaledPoint = (mFoldDrawHeight - scaledHeight) / 2.0f;
+ bottomScaledPoint = topScaledPoint + scaledHeight;
+
+ leftScaledPoint = (mFoldDrawWidth - scaledWidth) / 2.0f;
+ rightScaledPoint = leftScaledPoint + scaledWidth;
+
+ float anchorPoint = mIsHorizontal ? mAnchorFactor * mOriginalWidth :
+ mAnchorFactor * mOriginalHeight;
+
+ /* The fold along which the anchor point is located. */
+ float midFold = mIsHorizontal ? (anchorPoint / mFoldDrawWidth) : anchorPoint /
+ mFoldDrawHeight;
+
+ mSrc[0] = 0;
+ mSrc[1] = 0;
+ mSrc[2] = 0;
+ mSrc[3] = mFoldDrawHeight;
+ mSrc[4] = mFoldDrawWidth;
+ mSrc[5] = 0;
+ mSrc[6] = mFoldDrawWidth;
+ mSrc[7] = mFoldDrawHeight;
+
+ /* Computes the transformation matrix for each fold using the values calculated above. */
+ for (int x = 0; x < mNumberOfFolds; x++) {
+
+ boolean isEven = (x % 2 == 0);
+
+ if (mIsHorizontal) {
+ mDst[0] = (anchorPoint > x * mFoldDrawWidth) ? anchorPoint + (x - midFold) *
+ scaledWidth : anchorPoint - (midFold - x) * scaledWidth;
+ mDst[1] = isEven ? 0 : topScaledPoint;
+ mDst[2] = mDst[0];
+ mDst[3] = isEven ? mFoldDrawHeight: bottomScaledPoint;
+ mDst[4] = (anchorPoint > (x + 1) * mFoldDrawWidth) ? anchorPoint + (x + 1 - midFold)
+ * scaledWidth : anchorPoint - (midFold - x - 1) * scaledWidth;
+ mDst[5] = isEven ? topScaledPoint : 0;
+ mDst[6] = mDst[4];
+ mDst[7] = isEven ? bottomScaledPoint : mFoldDrawHeight;
+
+ } else {
+ mDst[0] = isEven ? 0 : leftScaledPoint;
+ mDst[1] = (anchorPoint > x * mFoldDrawHeight) ? anchorPoint + (x - midFold) *
+ scaledHeight : anchorPoint - (midFold - x) * scaledHeight;
+ mDst[2] = isEven ? leftScaledPoint: 0;
+ mDst[3] = (anchorPoint > (x + 1) * mFoldDrawHeight) ? anchorPoint + (x + 1 -
+ midFold) * scaledHeight : anchorPoint - (midFold - x - 1) * scaledHeight;
+ mDst[4] = isEven ? mFoldDrawWidth : rightScaledPoint;
+ mDst[5] = mDst[1];
+ mDst[6] = isEven ? rightScaledPoint : mFoldDrawWidth;
+ mDst[7] = mDst[3];
+ }
+
+ /* Pixel fractions are present for odd number of folds which need to be
+ * rounded off here.*/
+ for (int y = 0; y < 8; y ++) {
+ mDst[y] = Math.round(mDst[y]);
+ }
+
+ /* If it so happens that any of the folds have reached a point where
+ * the width or height of that fold is 0, then nothing needs to be
+ * drawn onto the canvas because the view is essentially completely
+ * folded.*/
+ if (mIsHorizontal) {
+ if (mDst[4] <= mDst[0] || mDst[6] <= mDst[2]) {
+ mShouldDraw = false;
+ return;
+ }
+ } else {
+ if (mDst[3] <= mDst[1] || mDst[7] <= mDst[5]) {
+ mShouldDraw = false;
+ return;
+ }
+ }
+
+ /* Sets the shadow and bitmap transformation matrices.*/
+ mMatrix[x].setPolyToPoly(mSrc, 0, mDst, 0, NUM_OF_POLY_POINTS / 2);
+ }
+ /* The shadows on the folds are split into two parts: Solid shadows and gradients.
+ * Every other fold has a solid shadow which overlays the whole fold. Similarly,
+ * the folds in between these alternating folds also have an overlaying shadow.
+ * However, it is a gradient that takes up part of the fold as opposed to a solid
+ * shadow overlaying the whole fold.*/
+
+ /* Solid shadow paint object. */
+ int alpha = (int) (mFoldFactor * 255 * SHADING_ALPHA);
+
+ mSolidShadow.setColor(Color.argb(alpha, 0, 0, 0));
+
+ if (mIsHorizontal) {
+ mShadowGradientMatrix.setScale(mFoldDrawWidth, 1);
+ mShadowLinearGradient.setLocalMatrix(mShadowGradientMatrix);
+ } else {
+ mShadowGradientMatrix.setScale(1, mFoldDrawHeight);
+ mShadowLinearGradient.setLocalMatrix(mShadowGradientMatrix);
+ }
+
+ mGradientShadow.setAlpha(alpha);
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ /** If prepareFold has not been called or if preparation has not completed yet,
+ * then no custom drawing will take place so only need to invoke super's
+ * onDraw and return. */
+ if (!mIsFoldPrepared || mFoldFactor == 0) {
+ super.dispatchDraw(canvas);
+ return;
+ }
+
+ if (!mShouldDraw) {
+ return;
+ }
+
+ Rect src;
+ /* Draws the bitmaps and shadows on the canvas with the appropriate transformations. */
+ for (int x = 0; x < mNumberOfFolds; x++) {
+
+ src = mFoldRectArray[x];
+ /* The canvas is saved and restored for every individual fold*/
+ canvas.save();
+
+ /* Concatenates the canvas with the transformation matrix for the
+ * the segment of the view corresponding to the actual image being
+ * displayed. */
+ canvas.concat(mMatrix[x]);
+ if (FoldingLayoutActivity.IS_JBMR2) {
+ mDstRect.set(0, 0, src.width(), src.height());
+ canvas.drawBitmap(mFullBitmap, src, mDstRect, null);
+ } else {
+ /* The same transformation matrix is used for both the shadow and the image
+ * segment. The canvas is clipped to account for the size of each fold and
+ * is translated so they are drawn in the right place. The shadow is then drawn on
+ * top of the different folds using the sametransformation matrix.*/
+ canvas.clipRect(0, 0, src.right - src.left, src.bottom - src.top);
+
+ if (mIsHorizontal) {
+ canvas.translate(-src.left, 0);
+ } else {
+ canvas.translate(0, -src.top);
+ }
+
+ super.dispatchDraw(canvas);
+
+ if (mIsHorizontal) {
+ canvas.translate(src.left, 0);
+ } else {
+ canvas.translate(0, src.top);
+ }
+ }
+ /* Draws the shadows corresponding to this specific fold. */
+ if (x % 2 == 0) {
+ canvas.drawRect(0, 0, mFoldDrawWidth, mFoldDrawHeight, mSolidShadow);
+ } else {
+ canvas.drawRect(0, 0, mFoldDrawWidth, mFoldDrawHeight, mGradientShadow);
+ }
+
+ canvas.restore();
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/samples/devbytes/graphics/FoldingLayout/src/com/example/android/foldinglayout/FoldingLayoutActivity.java b/samples/devbytes/graphics/FoldingLayout/src/com/example/android/foldinglayout/FoldingLayoutActivity.java
new file mode 100644
index 0000000..1a1033d
--- /dev/null
+++ b/samples/devbytes/graphics/FoldingLayout/src/com/example/android/foldinglayout/FoldingLayoutActivity.java
@@ -0,0 +1,429 @@
+/*
+ * 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.foldinglayout;
+
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.app.Activity;
+import android.graphics.Color;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
+import android.graphics.Paint;
+import android.graphics.SurfaceTexture;
+import android.hardware.Camera;
+import android.os.Build;
+import android.os.Bundle;
+import android.view.GestureDetector;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.view.TextureView;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.view.animation.AccelerateInterpolator;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemSelectedListener;
+import android.widget.ImageView;
+import android.widget.SeekBar;
+import android.widget.Spinner;
+
+import com.example.android.foldinglayout.FoldingLayout.Orientation;
+
+import java.io.IOException;
+
+/**
+ * This application creates a paper like folding effect of some view.
+ * The number of folds, orientation (vertical or horizontal) of the fold, and the
+ * anchor point about which the view will fold can be set to achieve different
+ * folding effects.
+ *
+ * Using bitmap and canvas scaling techniques, the foldingLayout can be scaled so as
+ * to depict a paper-like folding effect. The addition of shadows on the separate folds
+ * adds a sense of realism to the visual effect.
+ *
+ * This application shows folding of a TextureView containing a live camera feed,
+ * as well as the folding of an ImageView with a static image. The TextureView experiences
+ * jagged edges as a result of scaling operations on rectangles. The ImageView however
+ * contains a 1 pixel transparent border around its contents which can be used to avoid
+ * this unwanted artifact.
+ */
+public class FoldingLayoutActivity extends Activity {
+
+ private final int ANTIALIAS_PADDING = 1;
+
+ private final int FOLD_ANIMATION_DURATION = 1000;
+
+ /* A bug was introduced in Android 4.3 that ignores changes to the Canvas state
+ * between multiple calls to super.dispatchDraw() when running with hardware acceleration.
+ * To account for this bug, a slightly different approach was taken to fold a
+ * static image whereby a bitmap of the original contents is captured and drawn
+ * in segments onto the canvas. However, this method does not permit the folding
+ * of a TextureView hosting a live camera feed which continuously updates.
+ * Furthermore, the sepia effect was removed from the bitmap variation of the
+ * demo to simplify the logic when running with this workaround."
+ */
+ static final boolean IS_JBMR2 = Build.VERSION.SDK_INT == Build.VERSION_CODES.JELLY_BEAN_MR2;
+
+ private FoldingLayout mFoldLayout;
+ private SeekBar mAnchorSeekBar;
+ private Orientation mOrientation = Orientation.HORIZONTAL;
+
+ private int mTranslation = 0;
+ private int mNumberOfFolds = 2;
+ private int mParentPositionY = -1;
+ private int mTouchSlop = -1;
+
+ private float mAnchorFactor = 0;
+
+ private boolean mDidLoadSpinner = true;
+ private boolean mDidNotStartScroll = true;
+
+ private boolean mIsCameraFeed = false;
+ private boolean mIsSepiaOn = true;
+
+ private GestureDetector mScrollGestureDetector;
+ private ItemSelectedListener mItemSelectedListener;
+
+ private Camera mCamera;
+ private TextureView mTextureView;
+ private ImageView mImageView;
+
+ private Paint mSepiaPaint;
+ private Paint mDefaultPaint;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.activity_fold);
+
+ mImageView = (ImageView)findViewById(R.id.image_view);
+ mImageView.setPadding(ANTIALIAS_PADDING, ANTIALIAS_PADDING, ANTIALIAS_PADDING,
+ ANTIALIAS_PADDING);
+ mImageView.setScaleType(ImageView.ScaleType.FIT_XY);
+ mImageView.setImageDrawable(getResources().getDrawable(R.drawable.image));
+
+ mTextureView = new TextureView(this);
+ mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);
+
+ mAnchorSeekBar = (SeekBar)findViewById(R.id.anchor_seek_bar);
+ mFoldLayout = (FoldingLayout)findViewById(R.id.fold_view);
+ mFoldLayout.setBackgroundColor(Color.BLACK);
+ mFoldLayout.setFoldListener(mOnFoldListener);
+
+ mTouchSlop = ViewConfiguration.get(this).getScaledTouchSlop();
+
+ mAnchorSeekBar.setOnSeekBarChangeListener(mSeekBarChangeListener);
+
+ mScrollGestureDetector = new GestureDetector(this, new ScrollGestureDetector());
+ mItemSelectedListener = new ItemSelectedListener();
+
+ mDefaultPaint = new Paint();
+ mSepiaPaint = new Paint();
+
+ ColorMatrix m1 = new ColorMatrix();
+ ColorMatrix m2 = new ColorMatrix();
+ m1.setSaturation(0);
+ m2.setScale(1f, .95f, .82f, 1.0f);
+ m1.setConcat(m2, m1);
+ mSepiaPaint.setColorFilter(new ColorMatrixColorFilter(m1));
+ }
+
+ /**
+ * This listener, along with the setSepiaLayer method below, show a possible use case
+ * of the OnFoldListener provided with the FoldingLayout. This is a fun extra addition
+ * to the demo showing what kind of visual effects can be applied to the child of the
+ * FoldingLayout by setting the layer type to hardware. With a hardware layer type
+ * applied to the child, a paint object can also be applied to the same layer. Using
+ * the concatenation of two different color matrices (above), a color filter was created
+ * which simulates a sepia effect on the layer.*/
+ private OnFoldListener mOnFoldListener =
+ new OnFoldListener() {
+ @Override
+ public void onStartFold() {
+ if (mIsSepiaOn) {
+ setSepiaLayer(mFoldLayout.getChildAt(0), true);
+ }
+ }
+
+ @Override
+ public void onEndFold() {
+ setSepiaLayer(mFoldLayout.getChildAt(0), false);
+ }
+ };
+
+ private void setSepiaLayer (View view, boolean isSepiaLayerOn) {
+ if (!IS_JBMR2) {
+ if (isSepiaLayerOn) {
+ view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ view.setLayerPaint(mSepiaPaint);
+ } else {
+ view.setLayerPaint(mDefaultPaint);
+ }
+ }
+ }
+
+ /**
+ * Creates a SurfaceTextureListener in order to prepare a TextureView
+ * which displays a live, and continuously updated, feed from the Camera.
+ */
+ private TextureView.SurfaceTextureListener mSurfaceTextureListener = new TextureView
+ .SurfaceTextureListener() {
+ @Override
+ public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int i, int i2) {
+ mCamera = Camera.open();
+
+ if (mCamera == null && Camera.getNumberOfCameras() > 1) {
+ mCamera = mCamera.open(Camera.CameraInfo.CAMERA_FACING_FRONT);
+ }
+
+ if (mCamera == null) {
+ return;
+ }
+
+ try {
+ mCamera.setPreviewTexture(surfaceTexture);
+ mCamera.setDisplayOrientation(90);
+ mCamera.startPreview();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int i, int i2) {
+ // Ignored, Camera does all the work for us
+ }
+
+ @Override
+ public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
+ if (mCamera != null) {
+ mCamera.stopPreview();
+ mCamera.release();
+ }
+ return true;
+ }
+
+ @Override
+ public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {
+ // Invoked every time there's a new Camera preview frame
+ }
+ };
+
+ /**
+ * A listener for scrolling changes in the seekbar. The anchor point of the folding
+ * view is updated every time the seekbar stops tracking touch events. Every time the
+ * anchor point is updated, the folding view is restored to a default unfolded state.
+ */
+ private SeekBar.OnSeekBarChangeListener mSeekBarChangeListener = new SeekBar
+ .OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ mTranslation = 0;
+ mAnchorFactor = ((float)mAnchorSeekBar.getProgress())/100.0f;
+ mFoldLayout.setAnchorFactor(mAnchorFactor);
+ }
+ };
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ if (IS_JBMR2) {
+ getMenuInflater().inflate(R.menu.fold_with_bug, menu);
+ } else {
+ getMenuInflater().inflate(R.menu.fold, menu);
+ }
+ Spinner s = (Spinner) menu.findItem(R.id.num_of_folds).getActionView();
+ s.setOnItemSelectedListener(mItemSelectedListener);
+ return true;
+ }
+
+ @Override
+ public void onWindowFocusChanged (boolean hasFocus) {
+ super.onWindowFocusChanged(hasFocus);
+
+ int[] loc = new int[2];
+ mFoldLayout.getLocationOnScreen(loc);
+ mParentPositionY = loc[1];
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent me) {
+ return mScrollGestureDetector.onTouchEvent(me);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected (MenuItem item) {
+ switch(item.getItemId()) {
+ case R.id.animate_fold:
+ animateFold();
+ break;
+ case R.id.toggle_orientation:
+ mOrientation = (mOrientation == Orientation.HORIZONTAL) ? Orientation.VERTICAL :
+ Orientation.HORIZONTAL;
+ item.setTitle((mOrientation == Orientation.HORIZONTAL) ? R.string.vertical :
+ R.string.horizontal);
+ mTranslation = 0;
+ mFoldLayout.setOrientation(mOrientation);
+ break;
+ case R.id.camera_feed:
+ mIsCameraFeed = !mIsCameraFeed;
+ item.setTitle(mIsCameraFeed ? R.string.static_image : R.string.camera_feed);
+ item.setChecked(mIsCameraFeed);
+ if (mIsCameraFeed) {
+ mFoldLayout.removeView(mImageView);
+ mFoldLayout.addView(mTextureView, new ViewGroup.LayoutParams(
+ mFoldLayout.getWidth(), mFoldLayout.getHeight()));
+ } else {
+ mFoldLayout.removeView(mTextureView);
+ mFoldLayout.addView(mImageView, new ViewGroup.LayoutParams(
+ mFoldLayout.getWidth(), mFoldLayout.getHeight()));
+ }
+ mTranslation = 0;
+ break;
+ case R.id.sepia:
+ mIsSepiaOn = !mIsSepiaOn;
+ item.setChecked(!mIsSepiaOn);
+ if (mIsSepiaOn && mFoldLayout.getFoldFactor() != 0) {
+ setSepiaLayer(mFoldLayout.getChildAt(0), true);
+ } else {
+ setSepiaLayer(mFoldLayout.getChildAt(0), false);
+ }
+ break;
+ default:
+ break;
+
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ /**
+ * Animates the folding view inwards (to a completely folded state) from its
+ * current state and then back out to its original state.
+ */
+ public void animateFold ()
+ {
+ float foldFactor = mFoldLayout.getFoldFactor();
+
+ ObjectAnimator animator = ObjectAnimator.ofFloat(mFoldLayout, "foldFactor", foldFactor, 1);
+ animator.setRepeatMode(ValueAnimator.REVERSE);
+ animator.setRepeatCount(1);
+ animator.setDuration(FOLD_ANIMATION_DURATION);
+ animator.setInterpolator(new AccelerateInterpolator());
+ animator.start();
+ }
+
+ /**
+ * Listens for selection events of the spinner located on the action bar. Every
+ * time a new value is selected, the number of folds in the folding view is updated
+ * and is also restored to a default unfolded state.
+ */
+ private class ItemSelectedListener implements OnItemSelectedListener {
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
+ mNumberOfFolds = Integer.parseInt(parent.getItemAtPosition(pos).toString());
+ if (mDidLoadSpinner) {
+ mDidLoadSpinner = false;
+ } else {
+ mTranslation = 0;
+ mFoldLayout.setNumberOfFolds(mNumberOfFolds);
+ }
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> arg0) {
+ }
+ }
+
+ /** This class uses user touch events to fold and unfold the folding view. */
+ private class ScrollGestureDetector extends GestureDetector.SimpleOnGestureListener {
+ @Override
+ public boolean onDown (MotionEvent e) {
+ mDidNotStartScroll = true;
+ return true;
+ }
+
+ /**
+ * All the logic here is used to determine by what factor the paper view should
+ * be folded in response to the user's touch events. The logic here uses vertical
+ * scrolling to fold a vertically oriented view and horizontal scrolling to fold
+ * a horizontally oriented fold. Depending on where the anchor point of the fold is,
+ * movements towards or away from the anchor point will either fold or unfold
+ * the paper respectively.
+ *
+ * The translation logic here also accounts for the touch slop when a new user touch
+ * begins, but before a scroll event is first invoked.
+ */
+ @Override
+ public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
+ int touchSlop = 0;
+ float factor;
+ if (mOrientation == Orientation.VERTICAL) {
+ factor = Math.abs((float)(mTranslation) / (float)(mFoldLayout.getHeight()));
+
+ if (e2.getY() - mParentPositionY <= mFoldLayout.getHeight()
+ && e2.getY() - mParentPositionY >= 0) {
+ if ((e2.getY() - mParentPositionY) > mFoldLayout.getHeight() * mAnchorFactor) {
+ mTranslation -= (int)distanceY;
+ touchSlop = distanceY < 0 ? -mTouchSlop : mTouchSlop;
+ } else {
+ mTranslation += (int)distanceY;
+ touchSlop = distanceY < 0 ? mTouchSlop : -mTouchSlop;
+ }
+ mTranslation = mDidNotStartScroll ? mTranslation + touchSlop : mTranslation;
+
+ if (mTranslation < -mFoldLayout.getHeight()) {
+ mTranslation = -mFoldLayout.getHeight();
+ }
+ }
+ } else {
+ factor = Math.abs(((float)mTranslation) / ((float) mFoldLayout.getWidth()));
+
+ if (e2.getRawX() > mFoldLayout.getWidth() * mAnchorFactor) {
+ mTranslation -= (int)distanceX;
+ touchSlop = distanceX < 0 ? -mTouchSlop : mTouchSlop;
+ } else {
+ mTranslation += (int)distanceX;
+ touchSlop = distanceX < 0 ? mTouchSlop : -mTouchSlop;
+ }
+ mTranslation = mDidNotStartScroll ? mTranslation + touchSlop : mTranslation;
+
+ if (mTranslation < -mFoldLayout.getWidth()) {
+ mTranslation = -mFoldLayout.getWidth();
+ }
+ }
+
+ mDidNotStartScroll = false;
+
+ if (mTranslation > 0) {
+ mTranslation = 0;
+ }
+
+ mFoldLayout.setFoldFactor(factor);
+
+ return true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/samples/devbytes/graphics/FoldingLayout/src/com/example/android/foldinglayout/OnFoldListener.java b/samples/devbytes/graphics/FoldingLayout/src/com/example/android/foldinglayout/OnFoldListener.java
new file mode 100644
index 0000000..a305568
--- /dev/null
+++ b/samples/devbytes/graphics/FoldingLayout/src/com/example/android/foldinglayout/OnFoldListener.java
@@ -0,0 +1,27 @@
+/*
+ * 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.foldinglayout;
+
+/**
+ * This interface listens for when the folding layout begins folding (enters
+ * a folded state from a completely unfolded state), or ends folding (enters a
+ * completely unfolded state from a folded state).
+ */
+public interface OnFoldListener {
+ public void onStartFold();
+ public void onEndFold();
+}
diff --git a/samples/devbytes/graphics/ImagePixelization/AndroidManifest.xml b/samples/devbytes/graphics/ImagePixelization/AndroidManifest.xml
new file mode 100644
index 0000000..7bbb4f6
--- /dev/null
+++ b/samples/devbytes/graphics/ImagePixelization/AndroidManifest.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.android.imagepixelization"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <uses-sdk android:minSdkVersion="11"
+ android:targetSdkVersion="17"/>
+ <application android:label="@string/app_name" android:icon="@drawable/ic_launcher">
+ <activity android:name=".ImagePixelization"
+ android:label="@string/app_name">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/samples/devbytes/graphics/ImagePixelization/res/drawable-hdpi/ic_launcher.png b/samples/devbytes/graphics/ImagePixelization/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..96a442e
--- /dev/null
+++ b/samples/devbytes/graphics/ImagePixelization/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/graphics/ImagePixelization/res/drawable-hdpi/image.jpg b/samples/devbytes/graphics/ImagePixelization/res/drawable-hdpi/image.jpg
new file mode 100644
index 0000000..00bdbba
--- /dev/null
+++ b/samples/devbytes/graphics/ImagePixelization/res/drawable-hdpi/image.jpg
Binary files differ
diff --git a/samples/devbytes/graphics/ImagePixelization/res/drawable-ldpi/ic_launcher.png b/samples/devbytes/graphics/ImagePixelization/res/drawable-ldpi/ic_launcher.png
new file mode 100644
index 0000000..9923872
--- /dev/null
+++ b/samples/devbytes/graphics/ImagePixelization/res/drawable-ldpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/graphics/ImagePixelization/res/drawable-mdpi/ic_launcher.png b/samples/devbytes/graphics/ImagePixelization/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..359047d
--- /dev/null
+++ b/samples/devbytes/graphics/ImagePixelization/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/graphics/ImagePixelization/res/drawable-xhdpi/ic_launcher.png b/samples/devbytes/graphics/ImagePixelization/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..71c6d76
--- /dev/null
+++ b/samples/devbytes/graphics/ImagePixelization/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/graphics/ImagePixelization/res/layout/activity_image_pixelization.xml b/samples/devbytes/graphics/ImagePixelization/res/layout/activity_image_pixelization.xml
new file mode 100644
index 0000000..b3e30a8
--- /dev/null
+++ b/samples/devbytes/graphics/ImagePixelization/res/layout/activity_image_pixelization.xml
@@ -0,0 +1,42 @@
+<!-- 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.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingLeft="@dimen/activity_horizontal_margin"
+ android:paddingRight="@dimen/activity_horizontal_margin"
+ android:paddingTop="@dimen/activity_vertical_margin"
+ android:orientation="vertical">
+
+ <ImageView
+ android:id="@+id/pixelView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_alignParentTop="true"
+ android:layout_above="@+id/seekbar"
+ android:scaleType="fitXY"/>
+
+ <SeekBar
+ android:id="@+id/seekbar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="10dp"
+ android:max="@integer/seek_max"
+ android:progress="0"
+ android:layout_alignParentBottom = "true"/>
+
+</RelativeLayout>
+
diff --git a/samples/devbytes/graphics/ImagePixelization/res/menu/image_pixelization.xml b/samples/devbytes/graphics/ImagePixelization/res/menu/image_pixelization.xml
new file mode 100644
index 0000000..e6a8d66
--- /dev/null
+++ b/samples/devbytes/graphics/ImagePixelization/res/menu/image_pixelization.xml
@@ -0,0 +1,31 @@
+<!-- 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.
+-->
+<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+
+ <item android:id="@+id/builtin_pixelation_checkbox"
+ android:showAsAction="never"
+ android:title="@string/builtin_pixelization"
+ android:checkable="true" />
+
+ <item android:id="@+id/checkbox"
+ android:showAsAction="never"
+ android:title="@string/async_task"
+ android:checkable="true" />
+
+ <item android:id="@+id/animate"
+ android:showAsAction="never"
+ android:title="@string/animate_pixelation"/>
+
+</menu>
\ No newline at end of file
diff --git a/samples/devbytes/graphics/ImagePixelization/res/values/dimens.xml b/samples/devbytes/graphics/ImagePixelization/res/values/dimens.xml
new file mode 100644
index 0000000..09d93f5
--- /dev/null
+++ b/samples/devbytes/graphics/ImagePixelization/res/values/dimens.xml
@@ -0,0 +1,20 @@
+<!-- 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.
+-->
+<resources>
+
+ <dimen name="activity_horizontal_margin">16dp</dimen>
+ <dimen name="activity_vertical_margin">16dp</dimen>
+
+</resources>
diff --git a/samples/devbytes/graphics/ImagePixelization/res/values/integers.xml b/samples/devbytes/graphics/ImagePixelization/res/values/integers.xml
new file mode 100644
index 0000000..98c1438
--- /dev/null
+++ b/samples/devbytes/graphics/ImagePixelization/res/values/integers.xml
@@ -0,0 +1,19 @@
+<!-- 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.
+-->
+<resources>
+
+ <integer name="seek_max">1000</integer>
+
+</resources>
diff --git a/samples/devbytes/graphics/ImagePixelization/res/values/strings.xml b/samples/devbytes/graphics/ImagePixelization/res/values/strings.xml
new file mode 100644
index 0000000..35a87c8
--- /dev/null
+++ b/samples/devbytes/graphics/ImagePixelization/res/values/strings.xml
@@ -0,0 +1,22 @@
+<!-- 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.
+-->
+<resources>
+
+ <string name="app_name" >ImagePixelization</string>
+ <string name="animate_pixelation">Animate</string>
+ <string name="async_task">Using AyncTask</string>
+ <string name="builtin_pixelization">Built-in Pixelization</string>
+
+</resources>
diff --git a/samples/devbytes/graphics/ImagePixelization/src/com/example/android/imagepixelization/ImagePixelization.java b/samples/devbytes/graphics/ImagePixelization/src/com/example/android/imagepixelization/ImagePixelization.java
new file mode 100644
index 0000000..e31d95d
--- /dev/null
+++ b/samples/devbytes/graphics/ImagePixelization/src/com/example/android/imagepixelization/ImagePixelization.java
@@ -0,0 +1,315 @@
+/*
+ * 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.imagepixelization;
+
+import android.animation.ObjectAnimator;
+import android.app.Activity;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Color;
+import android.graphics.drawable.BitmapDrawable;
+import android.os.AsyncTask;
+import android.os.Build;
+import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.animation.LinearInterpolator;
+import android.widget.ImageView;
+import android.widget.SeekBar;
+
+import java.util.Arrays;
+
+/**
+ * This application shows three different graphics/animation concepts.
+ *
+ * A pixelization effect is applied to an image with varying pixelization
+ * factors to achieve an image that is pixelized to varying degrees. In
+ * order to optimize the amount of image processing performed on the image
+ * being pixelized, the pixelization effect only takes place if a predefined
+ * amount of time has elapsed since the main image was last pixelized. The
+ * effect is also applied when the user stops moving the seekbar.
+ *
+ * This application also shows how to use a ValueAnimator to achieve a
+ * smooth self-animating seekbar.
+ *
+ * Lastly, this application shows a use case of AsyncTask where some
+ * computation heavy processing can be moved onto a background thread,
+ * so as to keep the UI completely responsive to user input.
+ */
+public class ImagePixelization extends Activity {
+
+ final private static int SEEKBAR_ANIMATION_DURATION = 10000;
+ final private static int TIME_BETWEEN_TASKS = 400;
+ final private static int SEEKBAR_STOP_CHANGE_DELTA = 5;
+ final private static float PROGRESS_TO_PIXELIZATION_FACTOR = 4000.0f;
+
+ Bitmap mImageBitmap;
+ ImageView mImageView;
+ SeekBar mSeekBar;
+ boolean mIsChecked = false;
+ boolean mIsBuiltinPixelizationChecked = false;
+ int mLastProgress = 0;
+ long mLastTime = 0;
+ Bitmap mPixelatedBitmap;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_image_pixelization);
+
+ mImageView = (ImageView) findViewById(R.id.pixelView);
+ mSeekBar = (SeekBar)findViewById(R.id.seekbar);
+
+ mImageBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.image);
+ mImageView.setImageBitmap(mImageBitmap);
+
+ mSeekBar.setOnSeekBarChangeListener(mOnSeekBarChangeListener);
+ }
+
+ private SeekBar.OnSeekBarChangeListener mOnSeekBarChangeListener =
+ new SeekBar.OnSeekBarChangeListener() {
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ if (Math.abs(mSeekBar.getProgress() - mLastProgress) > SEEKBAR_STOP_CHANGE_DELTA) {
+ invokePixelization();
+ }
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ }
+
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ checkIfShouldPixelize();
+ }
+ };
+
+ /**
+ * Checks if enough time has elapsed since the last pixelization call was invoked.
+ * This prevents too many pixelization processes from being invoked at the same time
+ * while previous ones have not yet completed.
+ */
+ public void checkIfShouldPixelize() {
+ if ((System.currentTimeMillis() - mLastTime) > TIME_BETWEEN_TASKS) {
+ invokePixelization();
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.image_pixelization, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected (MenuItem item) {
+ switch (item.getItemId()){
+ case R.id.animate:
+ ObjectAnimator animator = ObjectAnimator.ofInt(mSeekBar, "progress", 0,
+ mSeekBar.getMax());
+ animator.setInterpolator(new LinearInterpolator());
+ animator.setDuration(SEEKBAR_ANIMATION_DURATION);
+ animator.start();
+ break;
+ case R.id.checkbox:
+ if (mIsChecked) {
+ item.setChecked(false);
+ mIsChecked = false;
+ } else {
+ item.setChecked(true);
+ mIsChecked = true;
+ }
+ break;
+ case R.id.builtin_pixelation_checkbox:
+ mIsBuiltinPixelizationChecked = !mIsBuiltinPixelizationChecked;
+ item.setChecked(mIsBuiltinPixelizationChecked);
+ break;
+ default:
+ break;
+ }
+ return true;
+ }
+
+ /**
+ * A simple pixelization algorithm. This uses a box blur algorithm where all the
+ * pixels within some region are averaged, and that average pixel value is then
+ * applied to all the pixels within that region. A higher pixelization factor
+ * imposes a smaller number of regions of greater size. Similarly, a smaller
+ * pixelization factor imposes a larger number of regions of smaller size.
+ */
+ public BitmapDrawable customImagePixelization(float pixelizationFactor, Bitmap bitmap) {
+
+ int width = bitmap.getWidth();
+ int height = bitmap.getHeight();
+
+ if (mPixelatedBitmap == null || !(width == mPixelatedBitmap.getWidth() && height ==
+ mPixelatedBitmap.getHeight())) {
+ mPixelatedBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ }
+
+ int xPixels = (int) (pixelizationFactor * ((float)width));
+ xPixels = xPixels > 0 ? xPixels : 1;
+ int yPixels = (int) (pixelizationFactor * ((float)height));
+ yPixels = yPixels > 0 ? yPixels : 1;
+ int pixel = 0, red = 0, green = 0, blue = 0, numPixels = 0;
+
+ int[] bitmapPixels = new int[width * height];
+ bitmap.getPixels(bitmapPixels, 0, width, 0, 0, width, height);
+
+ int[] pixels = new int[yPixels * xPixels];
+
+ int maxX, maxY;
+
+ for (int y = 0; y < height; y+=yPixels) {
+ for (int x = 0; x < width; x+=xPixels) {
+
+ numPixels = red = green = blue = 0;
+
+ maxX = Math.min(x + xPixels, width);
+ maxY = Math.min(y + yPixels, height);
+
+ for (int i = x; i < maxX; i++) {
+ for (int j = y; j < maxY; j++) {
+ pixel = bitmapPixels[j * width + i];
+ red += Color.red(pixel);
+ green += Color.green(pixel);
+ blue += Color.blue(pixel);
+ numPixels ++;
+ }
+ }
+
+ pixel = Color.rgb(red / numPixels, green / numPixels, blue / numPixels);
+
+ Arrays.fill(pixels, pixel);
+
+ int w = Math.min(xPixels, width - x);
+ int h = Math.min(yPixels, height - y);
+
+ mPixelatedBitmap.setPixels(pixels, 0 , w, x , y, w, h);
+ }
+ }
+
+ return new BitmapDrawable(getResources(), mPixelatedBitmap);
+ }
+
+ /**
+ * This method of image pixelization utilizes the bitmap scaling operations built
+ * into the framework. By downscaling the bitmap and upscaling it back to its
+ * original size (while setting the filter flag to false), the same effect can be
+ * achieved with much better performance.
+ */
+ public BitmapDrawable builtInPixelization(float pixelizationFactor, Bitmap bitmap) {
+
+ int width = bitmap.getWidth();
+ int height = bitmap.getHeight();
+
+ int downScaleFactorWidth = (int)(pixelizationFactor * width);
+ downScaleFactorWidth = downScaleFactorWidth > 0 ? downScaleFactorWidth : 1;
+ int downScaleFactorHeight = (int)(pixelizationFactor * height);
+ downScaleFactorHeight = downScaleFactorHeight > 0 ? downScaleFactorHeight : 1;
+
+ int downScaledWidth = width / downScaleFactorWidth;
+ int downScaledHeight = height / downScaleFactorHeight;
+
+ Bitmap pixelatedBitmap = Bitmap.createScaledBitmap(bitmap, downScaledWidth,
+ downScaledHeight, false);
+
+ /* Bitmap's createScaledBitmap method has a filter parameter that can be set to either
+ * true or false in order to specify either bilinear filtering or point sampling
+ * respectively when the bitmap is scaled up or now.
+ *
+ * Similarly, a BitmapDrawable also has a flag to specify the same thing. When the
+ * BitmapDrawable is applied to an ImageView that has some scaleType, the filtering
+ * flag is taken into consideration. However, for optimization purposes, this flag was
+ * ignored in BitmapDrawables before Jelly Bean MR1.
+ *
+ * Here, it is important to note that prior to JBMR1, two bitmap scaling operations
+ * are required to achieve the pixelization effect. Otherwise, a BitmapDrawable
+ * can be created corresponding to the downscaled bitmap such that when it is
+ * upscaled to fit the ImageView, the upscaling operation is a lot faster since
+ * it uses internal optimizations to fit the ImageView.
+ * */
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ BitmapDrawable bitmapDrawable = new BitmapDrawable(getResources(), pixelatedBitmap);
+ bitmapDrawable.setFilterBitmap(false);
+ return bitmapDrawable;
+ } else {
+ Bitmap upscaled = Bitmap.createScaledBitmap(pixelatedBitmap, width, height, false);
+ return new BitmapDrawable(getResources(), upscaled);
+ }
+ }
+
+ /**
+ * Invokes pixelization either on the main thread or on a background thread
+ * depending on whether or not the checkbox was checked.
+ */
+ public void invokePixelization () {
+ mLastTime = System.currentTimeMillis();
+ mLastProgress = mSeekBar.getProgress();
+ if (mIsChecked) {
+ PixelizeImageAsyncTask asyncPixelateTask = new PixelizeImageAsyncTask();
+ asyncPixelateTask.execute(mSeekBar.getProgress() / PROGRESS_TO_PIXELIZATION_FACTOR,
+ mImageBitmap);
+ } else {
+ mImageView.setImageDrawable(pixelizeImage(mSeekBar.getProgress()
+ / PROGRESS_TO_PIXELIZATION_FACTOR, mImageBitmap));
+ }
+ }
+
+ /**
+ * Selects either the custom pixelization algorithm that sets and gets bitmap
+ * pixels manually or the one that uses built-in bitmap operations.
+ */
+ public BitmapDrawable pixelizeImage(float pixelizationFactor, Bitmap bitmap) {
+ if (mIsBuiltinPixelizationChecked) {
+ return builtInPixelization(pixelizationFactor, bitmap);
+ } else {
+ return customImagePixelization(pixelizationFactor, bitmap);
+ }
+ }
+
+ /**
+ * Implementation of the AsyncTask class showing how to run the
+ * pixelization algorithm in the background, and retrieving the
+ * pixelated image from the resulting operation.
+ */
+ private class PixelizeImageAsyncTask extends AsyncTask<Object, Void, BitmapDrawable> {
+
+ @Override
+ protected BitmapDrawable doInBackground(Object... params) {
+ float pixelizationFactor = (Float)params[0];
+ Bitmap originalBitmap = (Bitmap)params[1];
+ return pixelizeImage(pixelizationFactor, originalBitmap);
+ }
+
+ @Override
+ protected void onPostExecute(BitmapDrawable result) {
+ mImageView.setImageDrawable(result);
+ }
+
+ @Override
+ protected void onPreExecute() {
+
+ }
+
+ @Override
+ protected void onProgressUpdate(Void... values) {
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/scripts/app_engine_server/redirects.yaml b/scripts/app_engine_server/redirects.yaml
index 7cb6379..465d692 100644
--- a/scripts/app_engine_server/redirects.yaml
+++ b/scripts/app_engine_server/redirects.yaml
@@ -279,6 +279,11 @@
type: permanent
comment: Open Accessory Protocol content has moved to source.android.com.
+- src: /tools/extras/support-library.html
+ dst: /tools/support-library/index.html
+ type: permanent
+ comment: moved Support Library doc to its own directory
+
- src: /guide/topics/usb
dst: /guide/topics/connectivity/usb
type: permanent